From 87e8f3d96642d4058fb3417bf0791a1faa77ed53 Mon Sep 17 00:00:00 2001 From: DELL Date: Fri, 7 Oct 2022 10:11:46 +0700 Subject: [PATCH 1/5] add delete blog --- dmoj/settings.py | 4 + dmoj/urls.py | 5 + judge/migrations/0132_auto_20220915_1837.py | 18 +++ judge/migrations/0133_merge_20220916_0636.py | 14 ++ judge/migrations/0134_blogpost_is_delete.py | 18 +++ .../0135_remove_blogpost_is_delete.py | 17 +++ judge/models/interface.py | 9 +- judge/views/organization.py | 65 +++++---- requirements.txt | 126 ++++++++++++------ templates/organization/blog/edit.html | 39 ++++++ templates/organization/form.html | 3 +- templates/user/base-users.html | 2 +- 12 files changed, 252 insertions(+), 68 deletions(-) create mode 100644 judge/migrations/0132_auto_20220915_1837.py create mode 100644 judge/migrations/0133_merge_20220916_0636.py create mode 100644 judge/migrations/0134_blogpost_is_delete.py create mode 100644 judge/migrations/0135_remove_blogpost_is_delete.py create mode 100644 templates/organization/blog/edit.html diff --git a/dmoj/settings.py b/dmoj/settings.py index 7a42b06..8aa3db7 100644 --- a/dmoj/settings.py +++ b/dmoj/settings.py @@ -405,6 +405,10 @@ DATABASES = { "default": { "ENGINE": "django.db.backends.sqlite3", "NAME": os.path.join(BASE_DIR, "db.sqlite3"), + 'USER': 'dmoj', + 'PASSWORD': 'YES', + 'HOST': 'localhost', + 'PORT': '', }, } diff --git a/dmoj/urls.py b/dmoj/urls.py index 68e691a..3f51c1b 100644 --- a/dmoj/urls.py +++ b/dmoj/urls.py @@ -665,6 +665,11 @@ urlpatterns = [ organization.EditOrganizationBlog.as_view(), name="edit_organization_blog", ), + # url( + # r"^/blog/deleting$", + # organization.PendingBlogs.as_view(), + # name="organization_pending_blogs", + # ), url( r"^/blog/pending$", organization.PendingBlogs.as_view(), diff --git a/judge/migrations/0132_auto_20220915_1837.py b/judge/migrations/0132_auto_20220915_1837.py new file mode 100644 index 0000000..ebaea8f --- /dev/null +++ b/judge/migrations/0132_auto_20220915_1837.py @@ -0,0 +1,18 @@ +# Generated by Django 2.2.28 on 2022-09-15 11:37 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('judge', '0131_auto_20220905_0027'), + ] + + operations = [ + migrations.AlterField( + model_name='profile', + name='timezone', + field=models.CharField(choices=[('Africa', [('Africa/Abidjan', 'Abidjan'), ('Africa/Accra', 'Accra'), ('Africa/Addis_Ababa', 'Addis_Ababa'), ('Africa/Algiers', 'Algiers'), ('Africa/Asmara', 'Asmara'), ('Africa/Asmera', 'Asmera'), ('Africa/Bamako', 'Bamako'), ('Africa/Bangui', 'Bangui'), ('Africa/Banjul', 'Banjul'), ('Africa/Bissau', 'Bissau'), ('Africa/Blantyre', 'Blantyre'), ('Africa/Brazzaville', 'Brazzaville'), ('Africa/Bujumbura', 'Bujumbura'), ('Africa/Cairo', 'Cairo'), ('Africa/Casablanca', 'Casablanca'), ('Africa/Ceuta', 'Ceuta'), ('Africa/Conakry', 'Conakry'), ('Africa/Dakar', 'Dakar'), ('Africa/Dar_es_Salaam', 'Dar_es_Salaam'), ('Africa/Djibouti', 'Djibouti'), ('Africa/Douala', 'Douala'), ('Africa/El_Aaiun', 'El_Aaiun'), ('Africa/Freetown', 'Freetown'), ('Africa/Gaborone', 'Gaborone'), ('Africa/Harare', 'Harare'), ('Africa/Johannesburg', 'Johannesburg'), ('Africa/Juba', 'Juba'), ('Africa/Kampala', 'Kampala'), ('Africa/Khartoum', 'Khartoum'), ('Africa/Kigali', 'Kigali'), ('Africa/Kinshasa', 'Kinshasa'), ('Africa/Lagos', 'Lagos'), ('Africa/Libreville', 'Libreville'), ('Africa/Lome', 'Lome'), ('Africa/Luanda', 'Luanda'), ('Africa/Lubumbashi', 'Lubumbashi'), ('Africa/Lusaka', 'Lusaka'), ('Africa/Malabo', 'Malabo'), ('Africa/Maputo', 'Maputo'), ('Africa/Maseru', 'Maseru'), ('Africa/Mbabane', 'Mbabane'), ('Africa/Mogadishu', 'Mogadishu'), ('Africa/Monrovia', 'Monrovia'), ('Africa/Nairobi', 'Nairobi'), ('Africa/Ndjamena', 'Ndjamena'), ('Africa/Niamey', 'Niamey'), ('Africa/Nouakchott', 'Nouakchott'), ('Africa/Ouagadougou', 'Ouagadougou'), ('Africa/Porto-Novo', 'Porto-Novo'), ('Africa/Sao_Tome', 'Sao_Tome'), ('Africa/Timbuktu', 'Timbuktu'), ('Africa/Tripoli', 'Tripoli'), ('Africa/Tunis', 'Tunis'), ('Africa/Windhoek', 'Windhoek')]), ('America', [('America/Adak', 'Adak'), ('America/Anchorage', 'Anchorage'), ('America/Anguilla', 'Anguilla'), ('America/Antigua', 'Antigua'), ('America/Araguaina', 'Araguaina'), ('America/Argentina/Buenos_Aires', 'Argentina/Buenos_Aires'), ('America/Argentina/Catamarca', 'Argentina/Catamarca'), ('America/Argentina/ComodRivadavia', 'Argentina/ComodRivadavia'), ('America/Argentina/Cordoba', 'Argentina/Cordoba'), ('America/Argentina/Jujuy', 'Argentina/Jujuy'), ('America/Argentina/La_Rioja', 'Argentina/La_Rioja'), ('America/Argentina/Mendoza', 'Argentina/Mendoza'), ('America/Argentina/Rio_Gallegos', 'Argentina/Rio_Gallegos'), ('America/Argentina/Salta', 'Argentina/Salta'), ('America/Argentina/San_Juan', 'Argentina/San_Juan'), ('America/Argentina/San_Luis', 'Argentina/San_Luis'), ('America/Argentina/Tucuman', 'Argentina/Tucuman'), ('America/Argentina/Ushuaia', 'Argentina/Ushuaia'), ('America/Aruba', 'Aruba'), ('America/Asuncion', 'Asuncion'), ('America/Atikokan', 'Atikokan'), ('America/Atka', 'Atka'), ('America/Bahia', 'Bahia'), ('America/Bahia_Banderas', 'Bahia_Banderas'), ('America/Barbados', 'Barbados'), ('America/Belem', 'Belem'), ('America/Belize', 'Belize'), ('America/Blanc-Sablon', 'Blanc-Sablon'), ('America/Boa_Vista', 'Boa_Vista'), ('America/Bogota', 'Bogota'), ('America/Boise', 'Boise'), ('America/Buenos_Aires', 'Buenos_Aires'), ('America/Cambridge_Bay', 'Cambridge_Bay'), ('America/Campo_Grande', 'Campo_Grande'), ('America/Cancun', 'Cancun'), ('America/Caracas', 'Caracas'), ('America/Catamarca', 'Catamarca'), ('America/Cayenne', 'Cayenne'), ('America/Cayman', 'Cayman'), ('America/Chicago', 'Chicago'), ('America/Chihuahua', 'Chihuahua'), ('America/Coral_Harbour', 'Coral_Harbour'), ('America/Cordoba', 'Cordoba'), ('America/Costa_Rica', 'Costa_Rica'), ('America/Creston', 'Creston'), ('America/Cuiaba', 'Cuiaba'), ('America/Curacao', 'Curacao'), ('America/Danmarkshavn', 'Danmarkshavn'), ('America/Dawson', 'Dawson'), ('America/Dawson_Creek', 'Dawson_Creek'), ('America/Denver', 'Denver'), ('America/Detroit', 'Detroit'), ('America/Dominica', 'Dominica'), ('America/Edmonton', 'Edmonton'), ('America/Eirunepe', 'Eirunepe'), ('America/El_Salvador', 'El_Salvador'), ('America/Ensenada', 'Ensenada'), ('America/Fort_Nelson', 'Fort_Nelson'), ('America/Fort_Wayne', 'Fort_Wayne'), ('America/Fortaleza', 'Fortaleza'), ('America/Glace_Bay', 'Glace_Bay'), ('America/Godthab', 'Godthab'), ('America/Goose_Bay', 'Goose_Bay'), ('America/Grand_Turk', 'Grand_Turk'), ('America/Grenada', 'Grenada'), ('America/Guadeloupe', 'Guadeloupe'), ('America/Guatemala', 'Guatemala'), ('America/Guayaquil', 'Guayaquil'), ('America/Guyana', 'Guyana'), ('America/Halifax', 'Halifax'), ('America/Havana', 'Havana'), ('America/Hermosillo', 'Hermosillo'), ('America/Indiana/Indianapolis', 'Indiana/Indianapolis'), ('America/Indiana/Knox', 'Indiana/Knox'), ('America/Indiana/Marengo', 'Indiana/Marengo'), ('America/Indiana/Petersburg', 'Indiana/Petersburg'), ('America/Indiana/Tell_City', 'Indiana/Tell_City'), ('America/Indiana/Vevay', 'Indiana/Vevay'), ('America/Indiana/Vincennes', 'Indiana/Vincennes'), ('America/Indiana/Winamac', 'Indiana/Winamac'), ('America/Indianapolis', 'Indianapolis'), ('America/Inuvik', 'Inuvik'), ('America/Iqaluit', 'Iqaluit'), ('America/Jamaica', 'Jamaica'), ('America/Jujuy', 'Jujuy'), ('America/Juneau', 'Juneau'), ('America/Kentucky/Louisville', 'Kentucky/Louisville'), ('America/Kentucky/Monticello', 'Kentucky/Monticello'), ('America/Knox_IN', 'Knox_IN'), ('America/Kralendijk', 'Kralendijk'), ('America/La_Paz', 'La_Paz'), ('America/Lima', 'Lima'), ('America/Los_Angeles', 'Los_Angeles'), ('America/Louisville', 'Louisville'), ('America/Lower_Princes', 'Lower_Princes'), ('America/Maceio', 'Maceio'), ('America/Managua', 'Managua'), ('America/Manaus', 'Manaus'), ('America/Marigot', 'Marigot'), ('America/Martinique', 'Martinique'), ('America/Matamoros', 'Matamoros'), ('America/Mazatlan', 'Mazatlan'), ('America/Mendoza', 'Mendoza'), ('America/Menominee', 'Menominee'), ('America/Merida', 'Merida'), ('America/Metlakatla', 'Metlakatla'), ('America/Mexico_City', 'Mexico_City'), ('America/Miquelon', 'Miquelon'), ('America/Moncton', 'Moncton'), ('America/Monterrey', 'Monterrey'), ('America/Montevideo', 'Montevideo'), ('America/Montreal', 'Montreal'), ('America/Montserrat', 'Montserrat'), ('America/Nassau', 'Nassau'), ('America/New_York', 'New_York'), ('America/Nipigon', 'Nipigon'), ('America/Nome', 'Nome'), ('America/Noronha', 'Noronha'), ('America/North_Dakota/Beulah', 'North_Dakota/Beulah'), ('America/North_Dakota/Center', 'North_Dakota/Center'), ('America/North_Dakota/New_Salem', 'North_Dakota/New_Salem'), ('America/Nuuk', 'Nuuk'), ('America/Ojinaga', 'Ojinaga'), ('America/Panama', 'Panama'), ('America/Pangnirtung', 'Pangnirtung'), ('America/Paramaribo', 'Paramaribo'), ('America/Phoenix', 'Phoenix'), ('America/Port-au-Prince', 'Port-au-Prince'), ('America/Port_of_Spain', 'Port_of_Spain'), ('America/Porto_Acre', 'Porto_Acre'), ('America/Porto_Velho', 'Porto_Velho'), ('America/Puerto_Rico', 'Puerto_Rico'), ('America/Punta_Arenas', 'Punta_Arenas'), ('America/Rainy_River', 'Rainy_River'), ('America/Rankin_Inlet', 'Rankin_Inlet'), ('America/Recife', 'Recife'), ('America/Regina', 'Regina'), ('America/Resolute', 'Resolute'), ('America/Rio_Branco', 'Rio_Branco'), ('America/Rosario', 'Rosario'), ('America/Santa_Isabel', 'Santa_Isabel'), ('America/Santarem', 'Santarem'), ('America/Santiago', 'Santiago'), ('America/Santo_Domingo', 'Santo_Domingo'), ('America/Sao_Paulo', 'Sao_Paulo'), ('America/Scoresbysund', 'Scoresbysund'), ('America/Shiprock', 'Shiprock'), ('America/Sitka', 'Sitka'), ('America/St_Barthelemy', 'St_Barthelemy'), ('America/St_Johns', 'St_Johns'), ('America/St_Kitts', 'St_Kitts'), ('America/St_Lucia', 'St_Lucia'), ('America/St_Thomas', 'St_Thomas'), ('America/St_Vincent', 'St_Vincent'), ('America/Swift_Current', 'Swift_Current'), ('America/Tegucigalpa', 'Tegucigalpa'), ('America/Thule', 'Thule'), ('America/Thunder_Bay', 'Thunder_Bay'), ('America/Tijuana', 'Tijuana'), ('America/Toronto', 'Toronto'), ('America/Tortola', 'Tortola'), ('America/Vancouver', 'Vancouver'), ('America/Virgin', 'Virgin'), ('America/Whitehorse', 'Whitehorse'), ('America/Winnipeg', 'Winnipeg'), ('America/Yakutat', 'Yakutat'), ('America/Yellowknife', 'Yellowknife')]), ('Antarctica', [('Antarctica/Casey', 'Casey'), ('Antarctica/Davis', 'Davis'), ('Antarctica/DumontDUrville', 'DumontDUrville'), ('Antarctica/Macquarie', 'Macquarie'), ('Antarctica/Mawson', 'Mawson'), ('Antarctica/McMurdo', 'McMurdo'), ('Antarctica/Palmer', 'Palmer'), ('Antarctica/Rothera', 'Rothera'), ('Antarctica/South_Pole', 'South_Pole'), ('Antarctica/Syowa', 'Syowa'), ('Antarctica/Troll', 'Troll'), ('Antarctica/Vostok', 'Vostok')]), ('Arctic', [('Arctic/Longyearbyen', 'Longyearbyen')]), ('Asia', [('Asia/Aden', 'Aden'), ('Asia/Almaty', 'Almaty'), ('Asia/Amman', 'Amman'), ('Asia/Anadyr', 'Anadyr'), ('Asia/Aqtau', 'Aqtau'), ('Asia/Aqtobe', 'Aqtobe'), ('Asia/Ashgabat', 'Ashgabat'), ('Asia/Ashkhabad', 'Ashkhabad'), ('Asia/Atyrau', 'Atyrau'), ('Asia/Baghdad', 'Baghdad'), ('Asia/Bahrain', 'Bahrain'), ('Asia/Baku', 'Baku'), ('Asia/Bangkok', 'Bangkok'), ('Asia/Barnaul', 'Barnaul'), ('Asia/Beirut', 'Beirut'), ('Asia/Bishkek', 'Bishkek'), ('Asia/Brunei', 'Brunei'), ('Asia/Calcutta', 'Calcutta'), ('Asia/Chita', 'Chita'), ('Asia/Choibalsan', 'Choibalsan'), ('Asia/Chongqing', 'Chongqing'), ('Asia/Chungking', 'Chungking'), ('Asia/Colombo', 'Colombo'), ('Asia/Dacca', 'Dacca'), ('Asia/Damascus', 'Damascus'), ('Asia/Dhaka', 'Dhaka'), ('Asia/Dili', 'Dili'), ('Asia/Dubai', 'Dubai'), ('Asia/Dushanbe', 'Dushanbe'), ('Asia/Famagusta', 'Famagusta'), ('Asia/Gaza', 'Gaza'), ('Asia/Harbin', 'Harbin'), ('Asia/Hebron', 'Hebron'), ('Asia/Ho_Chi_Minh', 'Ho_Chi_Minh'), ('Asia/Hong_Kong', 'Hong_Kong'), ('Asia/Hovd', 'Hovd'), ('Asia/Irkutsk', 'Irkutsk'), ('Asia/Istanbul', 'Istanbul'), ('Asia/Jakarta', 'Jakarta'), ('Asia/Jayapura', 'Jayapura'), ('Asia/Jerusalem', 'Jerusalem'), ('Asia/Kabul', 'Kabul'), ('Asia/Kamchatka', 'Kamchatka'), ('Asia/Karachi', 'Karachi'), ('Asia/Kashgar', 'Kashgar'), ('Asia/Kathmandu', 'Kathmandu'), ('Asia/Katmandu', 'Katmandu'), ('Asia/Khandyga', 'Khandyga'), ('Asia/Kolkata', 'Kolkata'), ('Asia/Krasnoyarsk', 'Krasnoyarsk'), ('Asia/Kuala_Lumpur', 'Kuala_Lumpur'), ('Asia/Kuching', 'Kuching'), ('Asia/Kuwait', 'Kuwait'), ('Asia/Macao', 'Macao'), ('Asia/Macau', 'Macau'), ('Asia/Magadan', 'Magadan'), ('Asia/Makassar', 'Makassar'), ('Asia/Manila', 'Manila'), ('Asia/Muscat', 'Muscat'), ('Asia/Nicosia', 'Nicosia'), ('Asia/Novokuznetsk', 'Novokuznetsk'), ('Asia/Novosibirsk', 'Novosibirsk'), ('Asia/Omsk', 'Omsk'), ('Asia/Oral', 'Oral'), ('Asia/Phnom_Penh', 'Phnom_Penh'), ('Asia/Pontianak', 'Pontianak'), ('Asia/Pyongyang', 'Pyongyang'), ('Asia/Qatar', 'Qatar'), ('Asia/Qostanay', 'Qostanay'), ('Asia/Qyzylorda', 'Qyzylorda'), ('Asia/Rangoon', 'Rangoon'), ('Asia/Riyadh', 'Riyadh'), ('Asia/Saigon', 'Saigon'), ('Asia/Sakhalin', 'Sakhalin'), ('Asia/Samarkand', 'Samarkand'), ('Asia/Seoul', 'Seoul'), ('Asia/Shanghai', 'Shanghai'), ('Asia/Singapore', 'Singapore'), ('Asia/Srednekolymsk', 'Srednekolymsk'), ('Asia/Taipei', 'Taipei'), ('Asia/Tashkent', 'Tashkent'), ('Asia/Tbilisi', 'Tbilisi'), ('Asia/Tehran', 'Tehran'), ('Asia/Tel_Aviv', 'Tel_Aviv'), ('Asia/Thimbu', 'Thimbu'), ('Asia/Thimphu', 'Thimphu'), ('Asia/Tokyo', 'Tokyo'), ('Asia/Tomsk', 'Tomsk'), ('Asia/Ujung_Pandang', 'Ujung_Pandang'), ('Asia/Ulaanbaatar', 'Ulaanbaatar'), ('Asia/Ulan_Bator', 'Ulan_Bator'), ('Asia/Urumqi', 'Urumqi'), ('Asia/Ust-Nera', 'Ust-Nera'), ('Asia/Vientiane', 'Vientiane'), ('Asia/Vladivostok', 'Vladivostok'), ('Asia/Yakutsk', 'Yakutsk'), ('Asia/Yangon', 'Yangon'), ('Asia/Yekaterinburg', 'Yekaterinburg'), ('Asia/Yerevan', 'Yerevan')]), ('Atlantic', [('Atlantic/Azores', 'Azores'), ('Atlantic/Bermuda', 'Bermuda'), ('Atlantic/Canary', 'Canary'), ('Atlantic/Cape_Verde', 'Cape_Verde'), ('Atlantic/Faeroe', 'Faeroe'), ('Atlantic/Faroe', 'Faroe'), ('Atlantic/Jan_Mayen', 'Jan_Mayen'), ('Atlantic/Madeira', 'Madeira'), ('Atlantic/Reykjavik', 'Reykjavik'), ('Atlantic/South_Georgia', 'South_Georgia'), ('Atlantic/St_Helena', 'St_Helena'), ('Atlantic/Stanley', 'Stanley')]), ('Australia', [('Australia/ACT', 'ACT'), ('Australia/Adelaide', 'Adelaide'), ('Australia/Brisbane', 'Brisbane'), ('Australia/Broken_Hill', 'Broken_Hill'), ('Australia/Canberra', 'Canberra'), ('Australia/Currie', 'Currie'), ('Australia/Darwin', 'Darwin'), ('Australia/Eucla', 'Eucla'), ('Australia/Hobart', 'Hobart'), ('Australia/LHI', 'LHI'), ('Australia/Lindeman', 'Lindeman'), ('Australia/Lord_Howe', 'Lord_Howe'), ('Australia/Melbourne', 'Melbourne'), ('Australia/NSW', 'NSW'), ('Australia/North', 'North'), ('Australia/Perth', 'Perth'), ('Australia/Queensland', 'Queensland'), ('Australia/South', 'South'), ('Australia/Sydney', 'Sydney'), ('Australia/Tasmania', 'Tasmania'), ('Australia/Victoria', 'Victoria'), ('Australia/West', 'West'), ('Australia/Yancowinna', 'Yancowinna')]), ('Brazil', [('Brazil/Acre', 'Acre'), ('Brazil/DeNoronha', 'DeNoronha'), ('Brazil/East', 'East'), ('Brazil/West', 'West')]), ('Canada', [('Canada/Atlantic', 'Atlantic'), ('Canada/Central', 'Central'), ('Canada/Eastern', 'Eastern'), ('Canada/Mountain', 'Mountain'), ('Canada/Newfoundland', 'Newfoundland'), ('Canada/Pacific', 'Pacific'), ('Canada/Saskatchewan', 'Saskatchewan'), ('Canada/Yukon', 'Yukon')]), ('Chile', [('Chile/Continental', 'Continental'), ('Chile/EasterIsland', 'EasterIsland')]), ('Etc', [('Etc/Greenwich', 'Greenwich'), ('Etc/UCT', 'UCT'), ('Etc/UTC', 'UTC'), ('Etc/Universal', 'Universal'), ('Etc/Zulu', 'Zulu')]), ('Europe', [('Europe/Amsterdam', 'Amsterdam'), ('Europe/Andorra', 'Andorra'), ('Europe/Astrakhan', 'Astrakhan'), ('Europe/Athens', 'Athens'), ('Europe/Belfast', 'Belfast'), ('Europe/Belgrade', 'Belgrade'), ('Europe/Berlin', 'Berlin'), ('Europe/Bratislava', 'Bratislava'), ('Europe/Brussels', 'Brussels'), ('Europe/Bucharest', 'Bucharest'), ('Europe/Budapest', 'Budapest'), ('Europe/Busingen', 'Busingen'), ('Europe/Chisinau', 'Chisinau'), ('Europe/Copenhagen', 'Copenhagen'), ('Europe/Dublin', 'Dublin'), ('Europe/Gibraltar', 'Gibraltar'), ('Europe/Guernsey', 'Guernsey'), ('Europe/Helsinki', 'Helsinki'), ('Europe/Isle_of_Man', 'Isle_of_Man'), ('Europe/Istanbul', 'Istanbul'), ('Europe/Jersey', 'Jersey'), ('Europe/Kaliningrad', 'Kaliningrad'), ('Europe/Kiev', 'Kiev'), ('Europe/Kirov', 'Kirov'), ('Europe/Kyiv', 'Kyiv'), ('Europe/Lisbon', 'Lisbon'), ('Europe/Ljubljana', 'Ljubljana'), ('Europe/London', 'London'), ('Europe/Luxembourg', 'Luxembourg'), ('Europe/Madrid', 'Madrid'), ('Europe/Malta', 'Malta'), ('Europe/Mariehamn', 'Mariehamn'), ('Europe/Minsk', 'Minsk'), ('Europe/Monaco', 'Monaco'), ('Europe/Moscow', 'Moscow'), ('Europe/Nicosia', 'Nicosia'), ('Europe/Oslo', 'Oslo'), ('Europe/Paris', 'Paris'), ('Europe/Podgorica', 'Podgorica'), ('Europe/Prague', 'Prague'), ('Europe/Riga', 'Riga'), ('Europe/Rome', 'Rome'), ('Europe/Samara', 'Samara'), ('Europe/San_Marino', 'San_Marino'), ('Europe/Sarajevo', 'Sarajevo'), ('Europe/Saratov', 'Saratov'), ('Europe/Simferopol', 'Simferopol'), ('Europe/Skopje', 'Skopje'), ('Europe/Sofia', 'Sofia'), ('Europe/Stockholm', 'Stockholm'), ('Europe/Tallinn', 'Tallinn'), ('Europe/Tirane', 'Tirane'), ('Europe/Tiraspol', 'Tiraspol'), ('Europe/Ulyanovsk', 'Ulyanovsk'), ('Europe/Uzhgorod', 'Uzhgorod'), ('Europe/Vaduz', 'Vaduz'), ('Europe/Vatican', 'Vatican'), ('Europe/Vienna', 'Vienna'), ('Europe/Vilnius', 'Vilnius'), ('Europe/Volgograd', 'Volgograd'), ('Europe/Warsaw', 'Warsaw'), ('Europe/Zagreb', 'Zagreb'), ('Europe/Zaporozhye', 'Zaporozhye'), ('Europe/Zurich', 'Zurich')]), ('Indian', [('Indian/Antananarivo', 'Antananarivo'), ('Indian/Chagos', 'Chagos'), ('Indian/Christmas', 'Christmas'), ('Indian/Cocos', 'Cocos'), ('Indian/Comoro', 'Comoro'), ('Indian/Kerguelen', 'Kerguelen'), ('Indian/Mahe', 'Mahe'), ('Indian/Maldives', 'Maldives'), ('Indian/Mauritius', 'Mauritius'), ('Indian/Mayotte', 'Mayotte'), ('Indian/Reunion', 'Reunion')]), ('Mexico', [('Mexico/BajaNorte', 'BajaNorte'), ('Mexico/BajaSur', 'BajaSur'), ('Mexico/General', 'General')]), ('Other', [('CET', 'CET'), ('CST6CDT', 'CST6CDT'), ('Cuba', 'Cuba'), ('EET', 'EET'), ('EST', 'EST'), ('EST5EDT', 'EST5EDT'), ('Egypt', 'Egypt'), ('Eire', 'Eire'), ('GB', 'GB'), ('GB-Eire', 'GB-Eire'), ('Greenwich', 'Greenwich'), ('HST', 'HST'), ('Hongkong', 'Hongkong'), ('Iceland', 'Iceland'), ('Iran', 'Iran'), ('Israel', 'Israel'), ('Jamaica', 'Jamaica'), ('Japan', 'Japan'), ('Kwajalein', 'Kwajalein'), ('Libya', 'Libya'), ('MET', 'MET'), ('MST', 'MST'), ('MST7MDT', 'MST7MDT'), ('NZ', 'NZ'), ('NZ-CHAT', 'NZ-CHAT'), ('Navajo', 'Navajo'), ('PRC', 'PRC'), ('PST8PDT', 'PST8PDT'), ('Poland', 'Poland'), ('Portugal', 'Portugal'), ('ROC', 'ROC'), ('ROK', 'ROK'), ('Singapore', 'Singapore'), ('Turkey', 'Turkey'), ('UCT', 'UCT'), ('UTC', 'UTC'), ('Universal', 'Universal'), ('W-SU', 'W-SU'), ('WET', 'WET'), ('Zulu', 'Zulu')]), ('Pacific', [('Pacific/Apia', 'Apia'), ('Pacific/Auckland', 'Auckland'), ('Pacific/Bougainville', 'Bougainville'), ('Pacific/Chatham', 'Chatham'), ('Pacific/Chuuk', 'Chuuk'), ('Pacific/Easter', 'Easter'), ('Pacific/Efate', 'Efate'), ('Pacific/Enderbury', 'Enderbury'), ('Pacific/Fakaofo', 'Fakaofo'), ('Pacific/Fiji', 'Fiji'), ('Pacific/Funafuti', 'Funafuti'), ('Pacific/Galapagos', 'Galapagos'), ('Pacific/Gambier', 'Gambier'), ('Pacific/Guadalcanal', 'Guadalcanal'), ('Pacific/Guam', 'Guam'), ('Pacific/Honolulu', 'Honolulu'), ('Pacific/Johnston', 'Johnston'), ('Pacific/Kanton', 'Kanton'), ('Pacific/Kiritimati', 'Kiritimati'), ('Pacific/Kosrae', 'Kosrae'), ('Pacific/Kwajalein', 'Kwajalein'), ('Pacific/Majuro', 'Majuro'), ('Pacific/Marquesas', 'Marquesas'), ('Pacific/Midway', 'Midway'), ('Pacific/Nauru', 'Nauru'), ('Pacific/Niue', 'Niue'), ('Pacific/Norfolk', 'Norfolk'), ('Pacific/Noumea', 'Noumea'), ('Pacific/Pago_Pago', 'Pago_Pago'), ('Pacific/Palau', 'Palau'), ('Pacific/Pitcairn', 'Pitcairn'), ('Pacific/Pohnpei', 'Pohnpei'), ('Pacific/Ponape', 'Ponape'), ('Pacific/Port_Moresby', 'Port_Moresby'), ('Pacific/Rarotonga', 'Rarotonga'), ('Pacific/Saipan', 'Saipan'), ('Pacific/Samoa', 'Samoa'), ('Pacific/Tahiti', 'Tahiti'), ('Pacific/Tarawa', 'Tarawa'), ('Pacific/Tongatapu', 'Tongatapu'), ('Pacific/Truk', 'Truk'), ('Pacific/Wake', 'Wake'), ('Pacific/Wallis', 'Wallis'), ('Pacific/Yap', 'Yap')]), ('US', [('US/Alaska', 'Alaska'), ('US/Aleutian', 'Aleutian'), ('US/Arizona', 'Arizona'), ('US/Central', 'Central'), ('US/East-Indiana', 'East-Indiana'), ('US/Eastern', 'Eastern'), ('US/Hawaii', 'Hawaii'), ('US/Indiana-Starke', 'Indiana-Starke'), ('US/Michigan', 'Michigan'), ('US/Mountain', 'Mountain'), ('US/Pacific', 'Pacific'), ('US/Samoa', 'Samoa')])], default='Asia/Ho_Chi_Minh', max_length=50, verbose_name='location'), + ), + ] diff --git a/judge/migrations/0133_merge_20220916_0636.py b/judge/migrations/0133_merge_20220916_0636.py new file mode 100644 index 0000000..c15532c --- /dev/null +++ b/judge/migrations/0133_merge_20220916_0636.py @@ -0,0 +1,14 @@ +# Generated by Django 2.2.28 on 2022-09-15 23:36 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('judge', '0132_auto_20220915_1837'), + ('judge', '0132_auto_20220915_1349'), + ] + + operations = [ + ] diff --git a/judge/migrations/0134_blogpost_is_delete.py b/judge/migrations/0134_blogpost_is_delete.py new file mode 100644 index 0000000..2cc4ec7 --- /dev/null +++ b/judge/migrations/0134_blogpost_is_delete.py @@ -0,0 +1,18 @@ +# Generated by Django 2.2.28 on 2022-10-06 16:08 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('judge', '0133_merge_20220916_0636'), + ] + + operations = [ + migrations.AddField( + model_name='blogpost', + name='is_delete', + field=models.BooleanField(default=False, verbose_name='is delete'), + ), + ] diff --git a/judge/migrations/0135_remove_blogpost_is_delete.py b/judge/migrations/0135_remove_blogpost_is_delete.py new file mode 100644 index 0000000..33a925c --- /dev/null +++ b/judge/migrations/0135_remove_blogpost_is_delete.py @@ -0,0 +1,17 @@ +# Generated by Django 2.2.28 on 2022-10-06 17:03 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('judge', '0134_blogpost_is_delete'), + ] + + operations = [ + migrations.RemoveField( + model_name='blogpost', + name='is_delete', + ), + ] diff --git a/judge/models/interface.py b/judge/models/interface.py index 24b05de..5f3fcee 100644 --- a/judge/models/interface.py +++ b/judge/models/interface.py @@ -1,4 +1,6 @@ +from email.policy import default import re +from tabnanny import verbose from django.core.exceptions import ValidationError from django.db import models @@ -96,7 +98,12 @@ class BlogPost(models.Model): return self.title def get_absolute_url(self): - return reverse("blog_post", args=(self.id, self.slug)) + try: + BlogPost.objects.get(id=self.id) + return reverse("blog_post", args=(self.id , self.slug)) + except BlogPost.DoesNotExist: + return reverse("organization_pending_blogs", + args=(self.organizations.first().pk, self.organizations.first().slug)) def can_see(self, user): if self.visible and self.publish_on <= timezone.now(): diff --git a/judge/views/organization.py b/judge/views/organization.py index a531d74..be54737 100644 --- a/judge/views/organization.py +++ b/judge/views/organization.py @@ -1,8 +1,10 @@ +from ast import Delete, arg from itertools import chain from django import forms from django.conf import settings from django.contrib import messages from django.contrib.auth.mixins import LoginRequiredMixin +from django.contrib.auth.models import User from django.core.cache import cache from django.core.cache.utils import make_template_fragment_key from django.core.exceptions import PermissionDenied @@ -17,7 +19,7 @@ from django.http import ( HttpResponseBadRequest, ) from django.shortcuts import get_object_or_404 -from django.urls import reverse +from django.urls import reverse , reverse_lazy from django.utils import timezone from django.utils.html import format_html from django.utils.functional import cached_property @@ -30,6 +32,7 @@ from django.views.generic import ( UpdateView, View, CreateView, + DeleteView, ) from django.views.generic.detail import ( SingleObjectMixin, @@ -126,12 +129,11 @@ class OrganizationMixin(OrganizationBase): context["logo_override_image"] = self.organization.logo_override_image if "organizations" in context: context.pop("organizations") - print(context) return context def dispatch(self, request, *args, **kwargs): try: - self.organization_id = int(kwargs["pk"]) + self.organization_id = int(kwargs["pk"] ) self.organization = get_object_or_404(Organization, id=self.organization_id) except Http404: key = kwargs.get(self.slug_url_kwarg, None) @@ -432,7 +434,6 @@ class OrganizationContests( self.set_editable_contest(contest) for contest in context["future_contests"]: self.set_editable_contest(contest) - print(context) return context @@ -1026,7 +1027,7 @@ class EditOrganizationBlog( MemberOrganizationMixin, UpdateView, ): - template_name = "organization/blog/add.html" + template_name = "organization/blog/edit.html" model = BlogPost def get_form_class(self): @@ -1052,6 +1053,10 @@ class EditOrganizationBlog( _("Not allowed to edit this blog"), ) + def delete_blog(self , request , *args , **kwargs): + self.blog_id = kwargs["blog_pk"] + BlogPost.objects.get(pk = self.blog_id).delete() + def get(self, request, *args, **kwargs): res = self.setup_blog(request, *args, **kwargs) if res: @@ -1062,7 +1067,13 @@ class EditOrganizationBlog( res = self.setup_blog(request, *args, **kwargs) if res: return res - return super().post(request, *args, **kwargs) + if request.POST['action'] == 'Delete': + self.create_notification("Delete blog") + self.delete_blog(request , *args , **kwargs) + cur_url = reverse("organization_pending_blogs", args=(self.organization_id,self.organization.slug) ) + return HttpResponseRedirect(cur_url) + else: + return super().post(request, *args, **kwargs) def get_object(self): return self.blog @@ -1070,32 +1081,36 @@ class EditOrganizationBlog( def get_title(self): return _("Edit blog %s") % self.object.title + def create_notification(self,action): + blog = BlogPost.objects.get(pk=self.blog_id) + link = reverse( + "edit_organization_blog", + args=[self.organization.id, self.organization.slug, self.blog_id], + ) + html = ( + f'{blog.title} - {self.organization.name}' + ) + post_authors = blog.authors.all() + posible_user = self.organization.admins.all() | post_authors + for user in posible_user: + if user.id == self.request.profile.id: + continue + notification = Notification( + owner=user, + author=self.request.profile, + category= action, + html_link=html, + ) + notification.save() + def form_valid(self, form): with transaction.atomic(), revisions.create_revision(): res = super(EditOrganizationBlog, self).form_valid(form) revisions.set_comment(_("Edited from site")) revisions.set_user(self.request.user) - - link = reverse( - "edit_organization_blog", - args=[self.organization.id, self.organization.slug, self.object.id], - ) - html = ( - f'{self.object.title} - {self.organization.name}' - ) - for user in self.organization.admins.all(): - if user.id == self.request.profile.id: - continue - notification = Notification( - owner=user, - author=self.request.profile, - category="Edit blog", - html_link=html, - ) - notification.save() + self.create_notification("Edit blog") return res - class PendingBlogs( LoginRequiredMixin, TitleMixin, diff --git a/requirements.txt b/requirements.txt index fdf6768..3fd918b 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,40 +1,86 @@ -Django>=2.2,<3 -django_compressor -django-mptt -django-pagedown -django-registration-redux -django-reversion -django-reversion-compare -django-social-share -django-sortedm2m @ git+https://github.com/DMOJ/django-sortedm2m.git -django-impersonate -dmoj-wpadmin @ git+https://github.com/LQDJudge/dmoj-wpadmin.git -lxml -Pygments -mistune<2 -social-auth-app-django -pytz -django-statici18n -pika -ua-parser -pyyaml -jinja2 -django_jinja -llist -requests -django-fernet-fields -pyotp -qrcode[pil] -jsonfield -pymoss -packaging -celery -ansi2html @ git+https://github.com/DMOJ/ansi2html.git -sqlparse -django-newsletter -netaddr -redis -lupa -websocket-client -python-memcached -numpy \ No newline at end of file +amqp==5.1.1 +ansi2html @ git+https://github.com/DMOJ/ansi2html.git@1791b4a468942eba19cb29242d9075f5e4284450 +async-timeout==4.0.2 +billiard==3.6.4.0 +celery==5.2.7 +certifi==2022.6.15.1 +cffi==1.15.1 +chardet==5.0.0 +charset-normalizer==2.1.1 +click==8.1.3 +click-didyoumean==0.3.0 +click-plugins==1.1.1 +click-repl==0.2.0 +cryptography==38.0.1 +defusedxml==0.7.1 +Deprecated==1.2.13 +diff-match-patch==20200713 +Django==2.2.28 +django-appconf==1.0.5 +django-compressor==4.1 +django-fernet-fields==0.6 +django-impersonate==1.8.2 +django-jinja==2.10.2 +django-js-asset==2.0.0 +django-mptt==0.13.4 +django-newsletter==0.9.1 +django-pagedown==2.2.1 +django-registration-redux==2.11 +django-reversion==5.0.2 +django-reversion-compare==0.15.0 +django-social-share==2.3.0 +django-sortedm2m==2.0.0 +django-statici18n==2.3.1 +dmoj-wpadmin==1.10.2 +Flask==2.2.2 +idna==3.3 +itsdangerous==2.1.2 +Jinja2==3.1.2 +jsonfield==3.1.0 +kombu==5.2.4 +ldif3==3.1.1 +llist==0.7.1 +lupa==1.13 +lxml==4.9.1 +MarkupSafe==2.1.1 +mistune==0.8.4 +mysqlclient==2.1.1 +netaddr==0.8.0 +numpy==1.23.3 +oauthlib==3.2.1 +packaging==21.3 +pika==1.3.0 +Pillow==9.2.0 +prompt-toolkit==3.0.31 +pycparser==2.21 +Pygments==2.13.0 +PyJWT==2.4.0 +pymoss==0.2 +pyotp==2.7.0 +pyparsing==3.0.9 +python-card-me==0.9.3 +python-dateutil==2.8.2 +python-memcached==1.59 +python3-openid==3.2.0 +pytz==2022.2.1 +PyYAML==6.0 +qrcode==7.3.1 +rcssmin==1.1.0 +redis==4.3.4 +requests==2.28.1 +requests-oauthlib==1.3.1 +rjsmin==1.2.0 +six==1.16.0 +social-auth-app-django==5.0.0 +social-auth-core==4.3.0 +sorl-thumbnail==12.9.0 +sqlparse==0.4.2 +surlex==0.2.0 +ua-parser==0.16.1 +unicodecsv==0.14.1 +urllib3==1.26.12 +vine==5.0.0 +wcwidth==0.2.5 +websocket-client==1.4.1 +Werkzeug==2.2.2 +wrapt==1.14.1 diff --git a/templates/organization/blog/edit.html b/templates/organization/blog/edit.html new file mode 100644 index 0000000..e2cca8e --- /dev/null +++ b/templates/organization/blog/edit.html @@ -0,0 +1,39 @@ +{% extends "organization/home-base.html" %} + +{% block three_col_js %} + {{ form.media.js }} + {% include "organization/home-js.html" %} +{% endblock %} + +{% block three_col_media %} + {{ form.media.css }} +{% endblock %} + +{% block middle_content %} +
+ {% csrf_token %} + {% if form.errors %} +
+ x + {{ form.non_field_errors() }} + {{ form.errors }} +
+ {% endif %} + {% for field in form %} + {% if not field.is_hidden %} +
+ {{ field.errors }} + +
+ {{ field }} +
+ {% if field.help_text %} + {{ field.help_text|safe }} + {% endif %} +
+ {% endif %} + {% endfor %} + + +
+{% endblock %} \ No newline at end of file diff --git a/templates/organization/form.html b/templates/organization/form.html index f23df3c..89241da 100644 --- a/templates/organization/form.html +++ b/templates/organization/form.html @@ -21,5 +21,6 @@ {% endif %} {% endfor %} - + + \ No newline at end of file diff --git a/templates/user/base-users.html b/templates/user/base-users.html index 608d8fc..e62c550 100644 --- a/templates/user/base-users.html +++ b/templates/user/base-users.html @@ -10,7 +10,7 @@ {% block users_media %}{% endblock %} {% endblock %} -{% block body %} +{% block body %}z
{% if page_obj and page_obj.num_pages > 1 %} From 11e4bc5a108d7fdc338cc6237be9624c3370c5ff Mon Sep 17 00:00:00 2001 From: DELL Date: Fri, 7 Oct 2022 14:07:53 +0700 Subject: [PATCH 2/5] edit BlogPost.get_absolute_url --- judge/models/interface.py | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/judge/models/interface.py b/judge/models/interface.py index 5f3fcee..a45c24a 100644 --- a/judge/models/interface.py +++ b/judge/models/interface.py @@ -98,13 +98,7 @@ class BlogPost(models.Model): return self.title def get_absolute_url(self): - try: - BlogPost.objects.get(id=self.id) - return reverse("blog_post", args=(self.id , self.slug)) - except BlogPost.DoesNotExist: - return reverse("organization_pending_blogs", - args=(self.organizations.first().pk, self.organizations.first().slug)) - + return reverse("blog_post", args=(self.id , self.slug)) def can_see(self, user): if self.visible and self.publish_on <= timezone.now(): if not self.is_organization_private: From 2646d12fe2b384ca6253f25d9dbaa96f7500e3ad Mon Sep 17 00:00:00 2001 From: Zhao-Linux Date: Sat, 8 Oct 2022 00:13:21 +0700 Subject: [PATCH 3/5] Fix error when save edit organization contest --- judge/views/organization.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/judge/views/organization.py b/judge/views/organization.py index daebc9f..3d3371c 100644 --- a/judge/views/organization.py +++ b/judge/views/organization.py @@ -933,8 +933,8 @@ class EditOrganizationContest( problem_form.save() for problem_form in problem_formset.deleted_objects: problem_form.delete() - return super().post(request, *args, **kwargs) - + super().post(request, *args, **kwargs) + return HttpResponseRedirect(reverse("organization_contests", args=(self.organization_id,self.organization.slug) )) self.object = self.contest return self.render_to_response( self.get_context_data( From dfc12f81f2de05f364567e33d1e040836d338fdf Mon Sep 17 00:00:00 2001 From: cuom1999 Date: Fri, 7 Oct 2022 12:17:07 -0500 Subject: [PATCH 4/5] Revert last 3 commits --- dmoj/urls.py | 30 ++-- judge/forms.py | 55 +++++- judge/jinja2/markdown/__init__.py | 2 +- judge/jinja2/spaceless.py | 3 +- judge/models/interface.py | 8 +- judge/models/problem_data.py | 12 +- judge/views/organization.py | 36 ++-- judge/views/problem.py | 10 +- judge/views/stats.py | 214 +++++++++++++++++------ requirements.txt | 127 +++++--------- templates/about/about.html | 1 + templates/base.html | 1 + templates/organization/contest/add.html | 52 ------ templates/organization/contest/edit.html | 88 ++++++++++ templates/stats/base.html | 9 +- templates/stats/language.html | 18 +- templates/stats/media-js.html | 20 +++ templates/stats/site.html | 65 +++++++ templates/stats/tab.html | 6 + templates/widgets/datetimepicker.html | 1 + 20 files changed, 486 insertions(+), 272 deletions(-) create mode 100644 templates/organization/contest/edit.html create mode 100644 templates/stats/site.html create mode 100644 templates/stats/tab.html diff --git a/dmoj/urls.py b/dmoj/urls.py index 3f51c1b..fcfd424 100644 --- a/dmoj/urls.py +++ b/dmoj/urls.py @@ -891,30 +891,22 @@ urlpatterns = [ "^language/", include( [ - url("^$", stats.language, name="language_stats"), url( - "^data/all/$", - stats.language_data, - name="language_stats_data_all", - ), - url( - "^data/ac/$", - stats.ac_language_data, - name="language_stats_data_ac", - ), - url( - "^data/status/$", - stats.status_data, - name="stats_data_status", - ), - url( - "^data/ac_rate/$", - stats.ac_rate, - name="language_stats_data_ac_rate", + "^$", + stats.StatLanguage.as_view(), + name="language_stats", ), ] ), ), + url( + "^site/", + include( + [ + url("^$", stats.StatSite.as_view(), name="site_stats"), + ] + ), + ), ] ), ), diff --git a/judge/forms.py b/judge/forms.py index 94516c5..f155d49 100644 --- a/judge/forms.py +++ b/judge/forms.py @@ -184,10 +184,49 @@ class AddOrganizationForm(ModelForm): return res -class OrganizationContestForm(ModelForm): +class AddOrganizationContestForm(ModelForm): + def __init__(self, *args, **kwargs): + self.request = kwargs.pop("request", None) + super(AddOrganizationContestForm, self).__init__(*args, **kwargs) + + def save(self, commit=True): + contest = super(AddOrganizationContestForm, self).save(commit=False) + old_save_m2m = self.save_m2m + + def save_m2m(): + for i, problem in enumerate(self.cleaned_data["problems"]): + contest_problem = ContestProblem( + contest=contest, problem=problem, points=100, order=i + 1 + ) + contest_problem.save() + contest.contest_problems.add(contest_problem) + old_save_m2m() + + self.save_m2m = save_m2m + contest.save() + self.save_m2m() + return contest + + class Meta: + model = Contest + fields = ( + "key", + "name", + "start_time", + "end_time", + "problems", + ) + widgets = { + "start_time": DateTimePickerWidget(), + "end_time": DateTimePickerWidget(), + "problems": HeavySelect2MultipleWidget(data_view="problem_select2"), + } + + +class EditOrganizationContestForm(ModelForm): def __init__(self, *args, **kwargs): self.org_id = kwargs.pop("org_id", 0) - super(OrganizationContestForm, self).__init__(*args, **kwargs) + super(EditOrganizationContestForm, self).__init__(*args, **kwargs) for field in [ "authors", "curators", @@ -203,27 +242,25 @@ class OrganizationContestForm(ModelForm): class Meta: model = Contest fields = ( + "is_visible", "key", "name", + "start_time", + "end_time", + "format_name", "authors", "curators", "testers", - "is_visible", + "time_limit", "use_clarifications", "hide_problem_tags", "scoreboard_visibility", "run_pretests_only", "points_precision", - "start_time", - "end_time", - "time_limit", "description", "og_image", "logo_override_image", "summary", - "format_name", - "format_config", - "problem_label_script", "access_code", "private_contestants", "view_contest_scoreboard", diff --git a/judge/jinja2/markdown/__init__.py b/judge/jinja2/markdown/__init__.py index 3387ddf..e22ae61 100644 --- a/judge/jinja2/markdown/__init__.py +++ b/judge/jinja2/markdown/__init__.py @@ -5,7 +5,7 @@ from urllib.parse import urlparse import mistune from django.conf import settings -from jinja2 import Markup +from markupsafe import Markup from lxml import html from lxml.etree import ParserError, XMLSyntaxError diff --git a/judge/jinja2/spaceless.py b/judge/jinja2/spaceless.py index 0644e89..01eb5d8 100644 --- a/judge/jinja2/spaceless.py +++ b/judge/jinja2/spaceless.py @@ -1,7 +1,8 @@ import re -from jinja2 import Markup, nodes +from jinja2 import nodes from jinja2.ext import Extension +from markupsafe import Markup class SpacelessExtension(Extension): diff --git a/judge/models/interface.py b/judge/models/interface.py index 5f3fcee..a45c24a 100644 --- a/judge/models/interface.py +++ b/judge/models/interface.py @@ -98,13 +98,7 @@ class BlogPost(models.Model): return self.title def get_absolute_url(self): - try: - BlogPost.objects.get(id=self.id) - return reverse("blog_post", args=(self.id , self.slug)) - except BlogPost.DoesNotExist: - return reverse("organization_pending_blogs", - args=(self.organizations.first().pk, self.organizations.first().slug)) - + return reverse("blog_post", args=(self.id , self.slug)) def can_see(self, user): if self.visible and self.publish_on <= timezone.now(): if not self.is_organization_private: diff --git a/judge/models/problem_data.py b/judge/models/problem_data.py index d1b9204..1717924 100644 --- a/judge/models/problem_data.py +++ b/judge/models/problem_data.py @@ -152,19 +152,21 @@ class ProblemData(models.Model): if e.errno != errno.ENOENT: raise if self.zipfile: - self.zipfile.name = _problem_directory_file(new, self.zipfile.name) + self.zipfile.name = problem_directory_file_helper(new, self.zipfile.name) if self.generator: - self.generator.name = _problem_directory_file(new, self.generator.name) + self.generator.name = problem_directory_file_helper( + new, self.generator.name + ) if self.custom_checker: - self.custom_checker.name = _problem_directory_file( + self.custom_checker.name = problem_directory_file_helper( new, self.custom_checker.name ) if self.custom_checker: - self.custom_checker.name = _problem_directory_file( + self.custom_checker.name = problem_directory_file_helper( new, self.custom_checker.name ) if self.custom_validator: - self.custom_validator.name = _problem_directory_file( + self.custom_validator.name = problem_directory_file_helper( new, self.custom_validator.name ) self.save() diff --git a/judge/views/organization.py b/judge/views/organization.py index be54737..3d3371c 100644 --- a/judge/views/organization.py +++ b/judge/views/organization.py @@ -47,8 +47,9 @@ from judge.forms import ( AddOrganizationMemberForm, OrganizationBlogForm, OrganizationAdminBlogForm, - OrganizationContestForm, + EditOrganizationContestForm, ContestProblemFormSet, + AddOrganizationContestForm, ) from judge.models import ( BlogPost, @@ -159,7 +160,9 @@ class OrganizationMixin(OrganizationBase): class AdminOrganizationMixin(OrganizationMixin): def dispatch(self, request, *args, **kwargs): res = super(AdminOrganizationMixin, self).dispatch(request, *args, **kwargs) - if self.can_edit_organization(self.organization): + if not hasattr(self, "organization") or self.can_edit_organization( + self.organization + ): return res return generic_message( request, @@ -172,7 +175,7 @@ class AdminOrganizationMixin(OrganizationMixin): class MemberOrganizationMixin(OrganizationMixin): def dispatch(self, request, *args, **kwargs): res = super(MemberOrganizationMixin, self).dispatch(request, *args, **kwargs) - if self.can_access(self.organization): + if not hasattr(self, "organization") or self.can_access(self.organization): return res return generic_message( request, @@ -389,18 +392,12 @@ class OrganizationContestMixin( OrganizationHomeViewContext, ): model = Contest - form_class = OrganizationContestForm def is_contest_editable(self, request, contest): return request.profile in contest.authors.all() or self.can_edit_organization( self.organization ) - def get_form_kwargs(self): - kwargs = super(OrganizationContestMixin, self).get_form_kwargs() - kwargs["org_id"] = self.organization.id - return kwargs - class OrganizationContests( OrganizationContestMixin, MemberOrganizationMixin, ContestList @@ -862,17 +859,26 @@ class AddOrganizationContest( AdminOrganizationMixin, OrganizationContestMixin, CreateView ): template_name = "organization/contest/add.html" + form_class = AddOrganizationContestForm def get_title(self): return _("Add contest") + def get_form_kwargs(self): + kwargs = super(AddOrganizationContest, self).get_form_kwargs() + kwargs["request"] = self.request + return kwargs + def form_valid(self, form): with transaction.atomic(), revisions.create_revision(): revisions.set_comment(_("Added from site")) revisions.set_user(self.request.user) + res = super(AddOrganizationContest, self).form_valid(form) + self.object.organizations.add(self.organization) self.object.is_organization_private = True + self.object.authors.add(self.request.profile) self.object.save() return res @@ -886,7 +892,8 @@ class AddOrganizationContest( class EditOrganizationContest( OrganizationContestMixin, MemberOrganizationMixin, UpdateView ): - template_name = "organization/contest/add.html" + template_name = "organization/contest/edit.html" + form_class = EditOrganizationContestForm def setup_contest(self, request, *args, **kwargs): contest_key = kwargs.get("contest", None) @@ -903,6 +910,11 @@ class EditOrganizationContest( status=400, ) + def get_form_kwargs(self): + kwargs = super(EditOrganizationContest, self).get_form_kwargs() + kwargs["org_id"] = self.organization.id + return kwargs + def get(self, request, *args, **kwargs): res = self.setup_contest(request, *args, **kwargs) if res: @@ -921,8 +933,8 @@ class EditOrganizationContest( problem_form.save() for problem_form in problem_formset.deleted_objects: problem_form.delete() - return super().post(request, *args, **kwargs) - + super().post(request, *args, **kwargs) + return HttpResponseRedirect(reverse("organization_contests", args=(self.organization_id,self.organization.slug) )) self.object = self.contest return self.render_to_response( self.get_context_data( diff --git a/judge/views/problem.py b/judge/views/problem.py index 9cf20e9..a328733 100644 --- a/judge/views/problem.py +++ b/judge/views/problem.py @@ -290,15 +290,19 @@ class ProblemDetail(ProblemMixin, SolvedProblemMixin, CommentedDetailView): except ObjectDoesNotExist: pass try: + print(self.request.LANGUAGE_CODE) + print(ProblemTranslation.objects.last().__dict__) translation = self.object.translations.get( language=self.request.LANGUAGE_CODE ) except ProblemTranslation.DoesNotExist: + print('DNE') context["title"] = self.object.name context["language"] = settings.LANGUAGE_CODE context["description"] = self.object.description context["translated"] = False else: + print('E') context["title"] = translation.name context["language"] = self.request.LANGUAGE_CODE context["description"] = translation.description @@ -845,8 +849,10 @@ class ProblemFeed(ProblemList): if self.feed_type == "new": return queryset.order_by("-date") elif user and self.feed_type == "volunteer": - voted_problems = user.volunteer_problem_votes.values_list( - "problem", flat=True + voted_problems = ( + user.volunteer_problem_votes.values_list("problem", flat=True) + if not bool(self.search_query) + else [] ) if self.show_solved_only: queryset = queryset.filter( diff --git a/judge/views/stats.py b/judge/views/stats.py index e8c74ef..5071dd5 100644 --- a/judge/views/stats.py +++ b/judge/views/stats.py @@ -1,5 +1,7 @@ from itertools import chain, repeat from operator import itemgetter +import json +import pandas as pd from django.conf import settings from django.db.models import Case, Count, FloatField, IntegerField, Value, When @@ -7,6 +9,10 @@ from django.db.models.expressions import CombinedExpression from django.http import JsonResponse from django.shortcuts import render from django.utils.translation import gettext as _ +from django.http import Http404 +from django.views.generic import TemplateView +from django.utils.safestring import mark_safe +from django.db import connection from judge.models import Language, Submission from judge.utils.stats import ( @@ -17,28 +23,34 @@ from judge.utils.stats import ( ) -ac_count = Count( - Case(When(submission__result="AC", then=Value(1)), output_field=IntegerField()) -) +class StatViewBase(TemplateView): + def get(self, request, *args, **kwargs): + if not request.user.is_superuser: + raise Http404 + return super().get(request, *args, **kwargs) -def repeat_chain(iterable): - return chain.from_iterable(repeat(iterable)) - - -def language_data( - request, language_count=Language.objects.annotate(count=Count("submission")) -): - languages = ( - language_count.filter(count__gt=0) - .values("key", "name", "count") - .order_by("-count") +class StatLanguage(StatViewBase): + template_name = "stats/language.html" + ac_count = Count( + Case(When(submission__result="AC", then=Value(1)), output_field=IntegerField()) ) - num_languages = min(len(languages), settings.DMOJ_STATS_LANGUAGE_THRESHOLD) - other_count = sum(map(itemgetter("count"), languages[num_languages:])) - return JsonResponse( - { + def repeat_chain(iterable): + return chain.from_iterable(repeat(iterable)) + + def language_data( + self, language_count=Language.objects.annotate(count=Count("submission")) + ): + languages = ( + language_count.filter(count__gt=0) + .values("key", "name", "count") + .order_by("-count") + ) + num_languages = min(len(languages), settings.DMOJ_STATS_LANGUAGE_THRESHOLD) + other_count = sum(map(itemgetter("count"), languages[num_languages:])) + + return { "labels": list(map(itemgetter("name"), languages[:num_languages])) + ["Other"], "datasets": [ @@ -50,53 +62,139 @@ def language_data( + [other_count], }, ], - }, - safe=False, - ) + } + def ac_language_data(self): + return self.language_data(Language.objects.annotate(count=self.ac_count)) -def ac_language_data(request): - return language_data(request, Language.objects.annotate(count=ac_count)) + def status_data(self, statuses=None): + if not statuses: + statuses = ( + Submission.objects.values("result") + .annotate(count=Count("result")) + .values("result", "count") + .order_by("-count") + ) + data = [] + for status in statuses: + res = status["result"] + if not res: + continue + count = status["count"] + data.append((str(Submission.USER_DISPLAY_CODES[res]), count)) + return get_pie_chart(data) -def status_data(request, statuses=None): - if not statuses: - statuses = ( - Submission.objects.values("result") - .annotate(count=Count("result")) - .values("result", "count") - .order_by("-count") + def ac_rate(self): + rate = CombinedExpression( + self.ac_count / Count("submission"), + "*", + Value(100.0), + output_field=FloatField(), ) - data = [] - for status in statuses: - res = status["result"] - if not res: - continue - count = status["count"] - data.append((str(Submission.USER_DISPLAY_CODES[res]), count)) + data = ( + Language.objects.annotate(total=Count("submission"), ac_rate=rate) + .filter(total__gt=0) + .order_by("total") + .values_list("name", "ac_rate") + ) + return get_bar_chart(list(data)) - return JsonResponse(get_pie_chart(data), safe=False) + def get_context_data(self, **kwargs): + context = super().get_context_data(**kwargs) + context["title"] = _("Language statistics") + context["tab"] = "language" + context["data_all"] = mark_safe(json.dumps(self.language_data())) + context["lang_ac"] = mark_safe(json.dumps(self.ac_language_data())) + context["status_counts"] = mark_safe(json.dumps(self.status_data())) + context["ac_rate"] = mark_safe(json.dumps(self.ac_rate())) + return context -def ac_rate(request): - rate = CombinedExpression( - ac_count / Count("submission"), "*", Value(100.0), output_field=FloatField() - ) - data = ( - Language.objects.annotate(total=Count("submission"), ac_rate=rate) - .filter(total__gt=0) - .order_by("total") - .values_list("name", "ac_rate") - ) - return JsonResponse(get_bar_chart(list(data))) +def group_data_by_period(dates, data, period="1W"): + df = pd.DataFrame() + df["dates"] = pd.to_datetime(dates) + df["data"] = data + df = df.groupby([pd.Grouper(key="dates", freq=period)])["data"].mean().reset_index() + return list(df["dates"]), list(df["data"]) -def language(request): - return render( - request, - "stats/language.html", - { - "title": _("Language statistics"), - "tab": "language", - }, - ) +class StatSite(StatViewBase): + template_name = "stats/site.html" + + def create_dataset(self, query, label): + labels = [] + data = [] + with connection.cursor() as cursor: + cursor.execute(query) + for row in cursor.fetchall(): + data.append(row[0]) + labels.append(row[1]) + + labels, data = group_data_by_period(labels, data, self.period) + + res = { + "labels": labels, + "datasets": [ + { + "label": label, + "data": data, + "borderColor": "rgb(75, 192, 192)", + "pointRadius": 1, + "borderWidth": 2, + } + ], + } + return mark_safe(json.dumps(res, default=str)) + + def get_submissions(self): + query = """ + SELECT COUNT(*), CAST(date AS Date) as day from judge_submission GROUP BY day; + """ + return self.create_dataset(query, _("Submissions")) + + def get_comments(self): + query = """ + SELECT COUNT(*), CAST(time AS Date) as day from judge_comment GROUP BY day; + """ + return self.create_dataset(query, _("Comments")) + + def get_new_users(self): + query = """ + SELECT COUNT(*), CAST(date_joined AS Date) as day from auth_user GROUP BY day; + """ + return self.create_dataset(query, _("New users")) + + def get_chat_messages(self): + query = """ + SELECT COUNT(*), CAST(time AS Date) as day from chat_box_message GROUP BY day; + """ + return self.create_dataset(query, _("Chat messages")) + + def get_contests(self): + query = """ + SELECT COUNT(*), CAST(start_time AS Date) as day from judge_contest GROUP BY day; + """ + return self.create_dataset(query, _("Contests")) + + def get_groups(self): + query = """ + SELECT COUNT(*), CAST(creation_date AS Date) as day from judge_organization GROUP BY day; + """ + return self.create_dataset(query, _("Groups")) + + def get(self, request, *args, **kwargs): + self.period = request.GET.get("period", "1W") + return super().get(request, *args, **kwargs) + + def get_context_data(self, **kwargs): + context = super().get_context_data(**kwargs) + context["tab"] = "site" + context["title"] = _("Site statistics") + context["submissions"] = self.get_submissions() + context["comments"] = self.get_comments() + context["new_users"] = self.get_new_users() + context["chat_messages"] = self.get_chat_messages() + context["contests"] = self.get_contests() + context["groups"] = self.get_groups() + return context diff --git a/requirements.txt b/requirements.txt index 3fd918b..b6bc5ee 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,86 +1,41 @@ -amqp==5.1.1 -ansi2html @ git+https://github.com/DMOJ/ansi2html.git@1791b4a468942eba19cb29242d9075f5e4284450 -async-timeout==4.0.2 -billiard==3.6.4.0 -celery==5.2.7 -certifi==2022.6.15.1 -cffi==1.15.1 -chardet==5.0.0 -charset-normalizer==2.1.1 -click==8.1.3 -click-didyoumean==0.3.0 -click-plugins==1.1.1 -click-repl==0.2.0 -cryptography==38.0.1 -defusedxml==0.7.1 -Deprecated==1.2.13 -diff-match-patch==20200713 -Django==2.2.28 -django-appconf==1.0.5 -django-compressor==4.1 -django-fernet-fields==0.6 -django-impersonate==1.8.2 -django-jinja==2.10.2 -django-js-asset==2.0.0 -django-mptt==0.13.4 -django-newsletter==0.9.1 -django-pagedown==2.2.1 -django-registration-redux==2.11 -django-reversion==5.0.2 -django-reversion-compare==0.15.0 -django-social-share==2.3.0 -django-sortedm2m==2.0.0 -django-statici18n==2.3.1 -dmoj-wpadmin==1.10.2 -Flask==2.2.2 -idna==3.3 -itsdangerous==2.1.2 -Jinja2==3.1.2 -jsonfield==3.1.0 -kombu==5.2.4 -ldif3==3.1.1 -llist==0.7.1 -lupa==1.13 -lxml==4.9.1 -MarkupSafe==2.1.1 -mistune==0.8.4 -mysqlclient==2.1.1 -netaddr==0.8.0 -numpy==1.23.3 -oauthlib==3.2.1 -packaging==21.3 -pika==1.3.0 -Pillow==9.2.0 -prompt-toolkit==3.0.31 -pycparser==2.21 -Pygments==2.13.0 -PyJWT==2.4.0 -pymoss==0.2 -pyotp==2.7.0 -pyparsing==3.0.9 -python-card-me==0.9.3 -python-dateutil==2.8.2 -python-memcached==1.59 -python3-openid==3.2.0 -pytz==2022.2.1 -PyYAML==6.0 -qrcode==7.3.1 -rcssmin==1.1.0 -redis==4.3.4 -requests==2.28.1 -requests-oauthlib==1.3.1 -rjsmin==1.2.0 -six==1.16.0 -social-auth-app-django==5.0.0 -social-auth-core==4.3.0 -sorl-thumbnail==12.9.0 -sqlparse==0.4.2 -surlex==0.2.0 -ua-parser==0.16.1 -unicodecsv==0.14.1 -urllib3==1.26.12 -vine==5.0.0 -wcwidth==0.2.5 -websocket-client==1.4.1 -Werkzeug==2.2.2 -wrapt==1.14.1 +Django>=2.2,<3 +django_compressor +django-mptt +django-pagedown +django-registration-redux +django-reversion +django-reversion-compare +django-social-share +django-sortedm2m @ git+https://github.com/DMOJ/django-sortedm2m.git +django-impersonate +dmoj-wpadmin @ git+https://github.com/LQDJudge/dmoj-wpadmin.git +lxml +Pygments +mistune<2 +social-auth-app-django +pytz +django-statici18n +pika +ua-parser +pyyaml +jinja2 +django_jinja +llist +requests +django-fernet-fields +pyotp +qrcode[pil] +jsonfield +pymoss +packaging +celery +ansi2html @ git+https://github.com/DMOJ/ansi2html.git +sqlparse +django-newsletter +netaddr +redis +lupa +websocket-client +python-memcached +numpy +pandas diff --git a/templates/about/about.html b/templates/about/about.html index 32fd390..1fc305e 100644 --- a/templates/about/about.html +++ b/templates/about/about.html @@ -5,6 +5,7 @@ LQDOJ (Le Quy Don Online Judge) là một trang web chấm bài tự động được phát triển dựa trên nền tảng mã nguồn mở DMOJ. Được xây dựng với mục đích ban đầu là tạo ra một môi trường học tập cho học sinh khối chuyên Tin trường THPT chuyên Lê Quý Đôn (TP Đà Nẵng), hiện nay LQDOJ đã cho phép đăng ký tự do để trở thành một sân chơi rộng mở cho toàn bộ cộng đồng học sinh yêu Tin học. Trang web cung cấp lượng bài luyện tập đồ sộ từ các kỳ thi HSG Quốc Gia, ACM ICPC, Olympic Duyên Hải Bắc Bộ, etc. cho đến các contest định kỳ để xếp loại khả năng (rating) giúp các bạn có thêm động lực cạnh tranh và khí thế phấn đấu rèn luyện nâng cao trình độ lập trình. Các bạn có thể tham khảo mã nguồn của trang web tại Github repo chính thức. Mọi ý kiến đóng góp và thắc mắc xin gửi về: diff --git a/templates/base.html b/templates/base.html index b925c04..d9f86c8 100644 --- a/templates/base.html +++ b/templates/base.html @@ -282,6 +282,7 @@ {% endif %} {% if request.user.is_superuser %}
  • {{ _('Internal') }}
  • +
  • {{ _('Stats') }}
  • {% endif %}
  • {{ _('Edit profile') }}
  • {% if request.user.is_impersonate %} diff --git a/templates/organization/contest/add.html b/templates/organization/contest/add.html index ff24057..516d77b 100644 --- a/templates/organization/contest/add.html +++ b/templates/organization/contest/add.html @@ -6,28 +6,6 @@ {% block three_col_media %} {{ form.media.css }} - {% endblock %} {% block middle_content %} @@ -55,36 +33,6 @@ {% endif %} {% endfor %} - {% if problems_form %} -

    - {{ problems_form.management_form }} - {{_('If you run out of rows, click Save')}} - - - - {% for field in problems_form[0] %} - {% if not field.is_hidden %} - - {% endif %} - {% endfor %} - - - - {% for form in problems_form %} - - {% for field in form %} - - {% endfor %} - - {% endfor %} - -
    - {{field.label}} -
    {{field}}
    {{field.errors}}
    - {% endif %} {% endblock %} \ No newline at end of file diff --git a/templates/organization/contest/edit.html b/templates/organization/contest/edit.html new file mode 100644 index 0000000..232b0c0 --- /dev/null +++ b/templates/organization/contest/edit.html @@ -0,0 +1,88 @@ +{% extends "organization/home-base.html" %} + +{% block three_col_js %} + {{ form.media.js }} +{% endblock %} + +{% block three_col_media %} + {{ form.media.css }} + +{% endblock %} + +{% block middle_content %} +
    + {% csrf_token %} + {% if form.errors %} +
    + x + {{ form.non_field_errors() }} + {{ form.errors }} +
    + {% endif %} + {% for field in form %} + {% if not field.is_hidden %} +
    + {{ field.errors }} + +
    + {{ field }} +
    + {% if field.help_text %} + {{ field.help_text|safe }} + {% endif %} +
    + {% endif %} + {% endfor %} + +

    + {{ problems_form.management_form }} + {{_('If you run out of rows, click Save')}} + + + + {% for field in problems_form[0] %} + {% if not field.is_hidden %} + + {% endif %} + {% endfor %} + + + + {% for form in problems_form %} + + {% for field in form %} + + {% endfor %} + + {% endfor %} + +
    + {{field.label}} +
    {{field}}
    {{field.errors}}
    + +
    +{% endblock %} \ No newline at end of file diff --git a/templates/stats/base.html b/templates/stats/base.html index d83ac96..6e72989 100644 --- a/templates/stats/base.html +++ b/templates/stats/base.html @@ -6,13 +6,10 @@ {% endcompress %} {% endblock %} -{% block content_title %}{{ _('Statistics') }}{% endblock %} +{% block content_title %}{% endblock %} +{% block title_ruler %}{% endblock %} {% block body %} - + {% include "stats/tab.html" %} {% block chart_body %}{% endblock %} {% endblock %} diff --git a/templates/stats/language.html b/templates/stats/language.html index d142c62..70ec589 100644 --- a/templates/stats/language.html +++ b/templates/stats/language.html @@ -36,20 +36,10 @@ Chart.defaults.global.tooltipFontFamily = Chart.defaults.global.tooltipTitleFontFamily = $('body').css('font-family'); - - function pie_chart(url, $chart) { - $.getJSON(url, function (data) { - draw_pie_chart(data, $chart); - }); - } - - pie_chart('{{ url('language_stats_data_all') }}', $('#lang-all')); - pie_chart('{{ url('language_stats_data_ac') }}', $('#lang-ac')); - pie_chart('{{ url('stats_data_status') }}', $('#status-counts')); - - $.getJSON('{{ url('language_stats_data_ac_rate') }}', function (data) { - draw_bar_chart(data, $('#ac-rate')); - }); + draw_pie_chart({{ data_all }}, $('#lang-all')); + draw_pie_chart({{ lang_ac }}, $('#lang-ac')); + draw_pie_chart({{ status_counts }}, $('#status-counts')); + draw_bar_chart({{ ac_rate }}, $('#ac-rate')); }); {% endblock %} diff --git a/templates/stats/media-js.html b/templates/stats/media-js.html index 69f9ad8..d6eb09d 100644 --- a/templates/stats/media-js.html +++ b/templates/stats/media-js.html @@ -87,4 +87,24 @@ }); return chart; } + + function draw_timeline(data, $chart) { + var ctx = $chart.find('canvas')[0].getContext('2d'); + var chart = new Chart(ctx, { + type: 'line', + data: data, + options: { + maintainAspectRatio: false, + legend: { + display: false, + }, + scales: { + xAxes: [{ + type: 'time', + }] + }, + }, + }); + return chart; + } diff --git a/templates/stats/site.html b/templates/stats/site.html new file mode 100644 index 0000000..aa67a07 --- /dev/null +++ b/templates/stats/site.html @@ -0,0 +1,65 @@ +{% extends "stats/base.html" %} +{% block media %} + +{% endblock %} + +{% block chart_body %} +
    +
    + +
    +

    {{ _('Submissions') }}

    +
    +
    +
    + +
    +

    {{ _('Contests') }}

    +
    +
    +
    + +
    +

    {{ _('New users') }}

    +
    +
    +
    + +
    +

    {{ _('Groups') }}

    +
    +
    +
    + +
    +

    {{ _('Comments') }}

    +
    +
    +
    + +
    +

    {{ _('Chat messages') }}

    +
    +{% endblock %} + +{% block bodyend %} + +{% endblock %} diff --git a/templates/stats/tab.html b/templates/stats/tab.html new file mode 100644 index 0000000..92ede48 --- /dev/null +++ b/templates/stats/tab.html @@ -0,0 +1,6 @@ +{% extends "tabs-base.html" %} + +{% block tabs %} + {{ make_tab('language', 'fa-list', url('language_stats'), _('Language')) }} + {{ make_tab('site', 'fa-list', url('site_stats'), _('Site')) }} +{% endblock %} \ No newline at end of file diff --git a/templates/widgets/datetimepicker.html b/templates/widgets/datetimepicker.html index 9363d07..e09b77a 100644 --- a/templates/widgets/datetimepicker.html +++ b/templates/widgets/datetimepicker.html @@ -1,4 +1,5 @@ Date: Fri, 7 Oct 2022 12:32:39 -0500 Subject: [PATCH 5/5] Revert --- dmoj/settings.py | 4 -- dmoj/urls.py | 5 --- judge/models/interface.py | 5 +-- judge/views/organization.py | 67 ++++++++++++-------------------- judge/views/problem.py | 4 -- requirements.txt | 2 +- templates/organization/form.html | 3 +- templates/user/base-users.html | 2 +- 8 files changed, 30 insertions(+), 62 deletions(-) diff --git a/dmoj/settings.py b/dmoj/settings.py index 8aa3db7..7a42b06 100644 --- a/dmoj/settings.py +++ b/dmoj/settings.py @@ -405,10 +405,6 @@ DATABASES = { "default": { "ENGINE": "django.db.backends.sqlite3", "NAME": os.path.join(BASE_DIR, "db.sqlite3"), - 'USER': 'dmoj', - 'PASSWORD': 'YES', - 'HOST': 'localhost', - 'PORT': '', }, } diff --git a/dmoj/urls.py b/dmoj/urls.py index fcfd424..255a951 100644 --- a/dmoj/urls.py +++ b/dmoj/urls.py @@ -665,11 +665,6 @@ urlpatterns = [ organization.EditOrganizationBlog.as_view(), name="edit_organization_blog", ), - # url( - # r"^/blog/deleting$", - # organization.PendingBlogs.as_view(), - # name="organization_pending_blogs", - # ), url( r"^/blog/pending$", organization.PendingBlogs.as_view(), diff --git a/judge/models/interface.py b/judge/models/interface.py index a45c24a..24b05de 100644 --- a/judge/models/interface.py +++ b/judge/models/interface.py @@ -1,6 +1,4 @@ -from email.policy import default import re -from tabnanny import verbose from django.core.exceptions import ValidationError from django.db import models @@ -98,7 +96,8 @@ class BlogPost(models.Model): return self.title def get_absolute_url(self): - return reverse("blog_post", args=(self.id , self.slug)) + return reverse("blog_post", args=(self.id, self.slug)) + def can_see(self, user): if self.visible and self.publish_on <= timezone.now(): if not self.is_organization_private: diff --git a/judge/views/organization.py b/judge/views/organization.py index 3d3371c..0414516 100644 --- a/judge/views/organization.py +++ b/judge/views/organization.py @@ -1,10 +1,8 @@ -from ast import Delete, arg from itertools import chain from django import forms from django.conf import settings from django.contrib import messages from django.contrib.auth.mixins import LoginRequiredMixin -from django.contrib.auth.models import User from django.core.cache import cache from django.core.cache.utils import make_template_fragment_key from django.core.exceptions import PermissionDenied @@ -19,7 +17,7 @@ from django.http import ( HttpResponseBadRequest, ) from django.shortcuts import get_object_or_404 -from django.urls import reverse , reverse_lazy +from django.urls import reverse from django.utils import timezone from django.utils.html import format_html from django.utils.functional import cached_property @@ -32,7 +30,6 @@ from django.views.generic import ( UpdateView, View, CreateView, - DeleteView, ) from django.views.generic.detail import ( SingleObjectMixin, @@ -134,7 +131,7 @@ class OrganizationMixin(OrganizationBase): def dispatch(self, request, *args, **kwargs): try: - self.organization_id = int(kwargs["pk"] ) + self.organization_id = int(kwargs["pk"]) self.organization = get_object_or_404(Organization, id=self.organization_id) except Http404: key = kwargs.get(self.slug_url_kwarg, None) @@ -933,8 +930,8 @@ class EditOrganizationContest( problem_form.save() for problem_form in problem_formset.deleted_objects: problem_form.delete() - super().post(request, *args, **kwargs) - return HttpResponseRedirect(reverse("organization_contests", args=(self.organization_id,self.organization.slug) )) + return super().post(request, *args, **kwargs) + self.object = self.contest return self.render_to_response( self.get_context_data( @@ -1039,7 +1036,7 @@ class EditOrganizationBlog( MemberOrganizationMixin, UpdateView, ): - template_name = "organization/blog/edit.html" + template_name = "organization/blog/add.html" model = BlogPost def get_form_class(self): @@ -1065,10 +1062,6 @@ class EditOrganizationBlog( _("Not allowed to edit this blog"), ) - def delete_blog(self , request , *args , **kwargs): - self.blog_id = kwargs["blog_pk"] - BlogPost.objects.get(pk = self.blog_id).delete() - def get(self, request, *args, **kwargs): res = self.setup_blog(request, *args, **kwargs) if res: @@ -1079,13 +1072,7 @@ class EditOrganizationBlog( res = self.setup_blog(request, *args, **kwargs) if res: return res - if request.POST['action'] == 'Delete': - self.create_notification("Delete blog") - self.delete_blog(request , *args , **kwargs) - cur_url = reverse("organization_pending_blogs", args=(self.organization_id,self.organization.slug) ) - return HttpResponseRedirect(cur_url) - else: - return super().post(request, *args, **kwargs) + return super().post(request, *args, **kwargs) def get_object(self): return self.blog @@ -1093,36 +1080,32 @@ class EditOrganizationBlog( def get_title(self): return _("Edit blog %s") % self.object.title - def create_notification(self,action): - blog = BlogPost.objects.get(pk=self.blog_id) - link = reverse( - "edit_organization_blog", - args=[self.organization.id, self.organization.slug, self.blog_id], - ) - html = ( - f'{blog.title} - {self.organization.name}' - ) - post_authors = blog.authors.all() - posible_user = self.organization.admins.all() | post_authors - for user in posible_user: - if user.id == self.request.profile.id: - continue - notification = Notification( - owner=user, - author=self.request.profile, - category= action, - html_link=html, - ) - notification.save() - def form_valid(self, form): with transaction.atomic(), revisions.create_revision(): res = super(EditOrganizationBlog, self).form_valid(form) revisions.set_comment(_("Edited from site")) revisions.set_user(self.request.user) - self.create_notification("Edit blog") + + link = reverse( + "edit_organization_blog", + args=[self.organization.id, self.organization.slug, self.object.id], + ) + html = ( + f'{self.object.title} - {self.organization.name}' + ) + for user in self.organization.admins.all(): + if user.id == self.request.profile.id: + continue + notification = Notification( + owner=user, + author=self.request.profile, + category="Edit blog", + html_link=html, + ) + notification.save() return res + class PendingBlogs( LoginRequiredMixin, TitleMixin, diff --git a/judge/views/problem.py b/judge/views/problem.py index a328733..ba9790c 100644 --- a/judge/views/problem.py +++ b/judge/views/problem.py @@ -290,19 +290,15 @@ class ProblemDetail(ProblemMixin, SolvedProblemMixin, CommentedDetailView): except ObjectDoesNotExist: pass try: - print(self.request.LANGUAGE_CODE) - print(ProblemTranslation.objects.last().__dict__) translation = self.object.translations.get( language=self.request.LANGUAGE_CODE ) except ProblemTranslation.DoesNotExist: - print('DNE') context["title"] = self.object.name context["language"] = settings.LANGUAGE_CODE context["description"] = self.object.description context["translated"] = False else: - print('E') context["title"] = translation.name context["language"] = self.request.LANGUAGE_CODE context["description"] = translation.description diff --git a/requirements.txt b/requirements.txt index b6bc5ee..0743316 100644 --- a/requirements.txt +++ b/requirements.txt @@ -38,4 +38,4 @@ lupa websocket-client python-memcached numpy -pandas +pandas \ No newline at end of file diff --git a/templates/organization/form.html b/templates/organization/form.html index 89241da..f23df3c 100644 --- a/templates/organization/form.html +++ b/templates/organization/form.html @@ -21,6 +21,5 @@
    {% endif %} {% endfor %} - - + \ No newline at end of file diff --git a/templates/user/base-users.html b/templates/user/base-users.html index e62c550..608d8fc 100644 --- a/templates/user/base-users.html +++ b/templates/user/base-users.html @@ -10,7 +10,7 @@ {% block users_media %}{% endblock %} {% endblock %} -{% block body %}z +{% block body %}
    {% if page_obj and page_obj.num_pages > 1 %}