diff --git a/.gitea/workflows/deploy_stage.yml b/.gitea/workflows/deploy_stage.yml index c780d97..70f79f9 100644 --- a/.gitea/workflows/deploy_stage.yml +++ b/.gitea/workflows/deploy_stage.yml @@ -5,15 +5,17 @@ on: - stage workflow_dispatch: +env: + DEPLOY_DIR: "/var/www/touren.alpenverein-karlsruhe.de/wsgi/django-dav-events.stage" + jobs: - make-deploy: + deploy-on-stage: name: Deploy into stage environment runs-on: [django-dav-events, kitty] steps: - - name: "Checkout stage branch of the repository" - run: git clone -b stage "${{ gitea.server_url }}/${{ gitea.repository }}" ./repository - - name: "Run make deploy" - working-directory: ./repository - env: - DEPLOY_DIR: "/var/www/touren.alpenverein-karlsruhe.de/wsgi/django-dav-events.stage" - run: make deploy + - name: "Migrate database" + run: $DEPLOY_DIR/python/bin/python $DEPLOY_DIR/django/manage.py migrate --noinput + - name: "Collect static files" + run: $DEPLOY_DIR/python/bin/python $DEPLOY_DIR/django/manage.py collectstatic --noinput + - name: "Touch wsgi file" + run: touch $DEPLOY_DIR/django/main/wsgi.py diff --git a/INSTALL.rst b/INSTALL.rst index f20a374..bb3207e 100644 --- a/INSTALL.rst +++ b/INSTALL.rst @@ -1,19 +1,27 @@ REQUIREMENTS ============ -- Python 3 -- Python package virtualenv (in most cases this is distributed or installed together with python) -- Django (will be installed automatically) -- Several additional django related python packages (will be installed automatically) +- Python >= 3.12 +- Django and some other python packages, that will be installed throughout + the installation process For production use you surly want a real web server that supports WSGI -(e.g. Apache httpd with mod_wsgi) and a real Database like PostgreSQL. +(e.g. Apache httpd with mod_wsgi) and a real Database like PostgreSQL (psycopg2). QUICK INSTALLATION FOR THE IMPATIENT ==================================== -- python setup.py mkpyenv +- python -m venv ./etc/python - source env/python/bin/activate -- python setup.py quickdev +- python -m pip install -r requirements.txt +- python -m pip install -e . +- django-dav-events-admin setup ./env/django +- python ./env/django/manage.py enable_module dav_auth +- python ./env/django/manage.py enable_module dav_events +- python ./env/django/manage.py enable_module dav_registration +- python ./env/django/manage.py enable_module dav_event_office +- python ./env/django/manage.py makemigrations +- python ./env/django/manage.py migrate +- python ./env/django/manage.py createsuperuser INSTALLATION @@ -24,14 +32,9 @@ INSTALLATION It is strongly recommended to create a separated python environment for this django project. But it is not exactly necessary. -The creation of a separated python environment is very easy with the -virtualenv tool (a python package). - -If you decide to not use virtualenv, proceed with step 2. - - Create the python environment in a directory called ./env/python: - ``virtualenv --prompt="(dav)" ./env/python`` + ``python -m venv --prompt="(dav)" ./env/python`` - If you use a posix compatible shell (like bash, the linux default shell), you have to activate the environment for the current shell session @@ -50,16 +53,29 @@ If you have left the session or deactivated the environment and want to reactivate the environment (e.g. to execute a python command) use the previous ``source ...`` command. -2. Install files ----------------- +2. Install requirements +----------------------- + +- ``python -m pip install -r requirements.txt`` + +3. Install project code in development mode +------------------------------------------- + +- ``python -m pip install -e .`` + +4. Setup django project root +---------------------------- + +To run a django app, you need a django project root directory, with some +static and variable files in it. +In the last step a tool was installed, that can be used to create such +a project directory with all the neccessary subdirectories and files. +Our example will create the django project in ./etc/django and we will +call this directory *project root* for now on. -- ``python setup.py develop`` - ``django-dav-events-admin setup ./env/django`` -The django project directory ('./env/django' within the previous example) -will be called *project root* for now on. - -3. Enable modules +5. Enable modules ----------------- Our web application consist of several modules, that care about single aspects of the whole picture. @@ -71,6 +87,8 @@ and run - ``python manage.py enable_module dav_auth`` - ``python manage.py enable_module dav_events`` +- ``python manage.py enable_module dav_registration`` +- ``python manage.py enable_module dav_event_office`` 4. Create the database schema / Populate the database ----------------------------------------------------- @@ -94,3 +112,12 @@ While you still are in the *project root* directory, run Now you should be able to connect to the test server via http://localhost:8000 + +7. Configure production web server +---------------------------------- +For production use you do not want to run the test server, +but have a real web server like apache or nginx running the +django app via the WSGI interface. +The entry point for your WSGI server is the file +``main/wsgi.py`` within the *project root* directory. + diff --git a/Makefile b/Makefile deleted file mode 100644 index d2b8c8b..0000000 --- a/Makefile +++ /dev/null @@ -1,20 +0,0 @@ -PYTHON := python - -DEPLOY_DIR ?= /var/www/wsgi/django-dav-events - -.PHONY: default help test deploy - -default: help - -help: - @echo "The make stuff is used by our CI/CD buildbot." - -test: - tox - -deploy: - git -C "$(DEPLOY_DIR)/src/django-dav-events" pull - "$(DEPLOY_DIR)/python/bin/python" "$(DEPLOY_DIR)/django/manage.py" migrate --noinput - "$(DEPLOY_DIR)/python/bin/python" "$(DEPLOY_DIR)/django/manage.py" collectstatic --noinput - touch "$(DEPLOY_DIR)/django/main/wsgi.py" - diff --git a/README.rst b/README.rst index b1d97bc..12cb170 100644 --- a/README.rst +++ b/README.rst @@ -1,6 +1,8 @@ ABOUT ===== -This is the DAV Events django project. +django-dav-events is a django based web app to drive the +"Touren- & Kurseportal" of +"Sektion Karlsruhe im Deutschen Alpenverein (DAV) e.V." REQUIREMENTS diff --git a/dav_auth/tests/generic.py b/dav_auth/tests/generic.py index ace764a..eede63a 100644 --- a/dav_auth/tests/generic.py +++ b/dav_auth/tests/generic.py @@ -9,7 +9,7 @@ class SeleniumAuthMixin: username_field = self.wait_on_presence(driver, (By.ID, 'id_username')) username_field.clear() username_field.send_keys(username) - password_field = driver.find_element_by_id('id_password') + password_field = driver.find_element(By.ID, 'id_password') password_field.clear() password_field.send_keys(password) password_field.send_keys(Keys.RETURN) diff --git a/dav_auth/tests/test_screenshots.py b/dav_auth/tests/test_screenshots.py index 168476a..9d30265 100644 --- a/dav_auth/tests/test_screenshots.py +++ b/dav_auth/tests/test_screenshots.py @@ -31,25 +31,25 @@ class TestCase(ScreenshotTestCase): # Go to login page via login button on root page -> save plain login form c = self.selenium c.get(self.complete_url('/')) - link = c.find_element_by_css_selector('#login-widget a') + link = c.find_element(By.CSS_SELECTOR, '#login-widget a') link.click() self.wait_on_presence(c, (By.ID, 'id_username')) self.save_screenshot('empty_login_form', sequence=sequence_name) # Fill in a password -> Save dots in password field - username_field = c.find_element_by_id('id_username') + username_field = c.find_element(By.ID, 'id_username') username_field.clear() username_field.send_keys('username') - password_field = c.find_element_by_id('id_password') + password_field = c.find_element(By.ID, 'id_password') password_field.clear() password_field.send_keys(self.test_password) self.save_screenshot('filled_login_form', sequence=sequence_name) # Wrong username -> save error message - username_field = c.find_element_by_id('id_username') + username_field = c.find_element(By.ID, 'id_username') username_field.clear() username_field.send_keys(self.test_username[::-1]) - password_field = c.find_element_by_id('id_password') + password_field = c.find_element(By.ID, 'id_password') password_field.clear() password_field.send_keys(self.test_password) password_field.send_keys(Keys.RETURN) @@ -58,10 +58,10 @@ class TestCase(ScreenshotTestCase): alert_button.click() # Wrong password -> save error message - username_field = c.find_element_by_id('id_username') + username_field = c.find_element(By.ID, 'id_username') username_field.clear() username_field.send_keys(self.test_username) - password_field = c.find_element_by_id('id_password') + password_field = c.find_element(By.ID, 'id_password') password_field.clear() password_field.send_keys(self.test_password[::-1]) password_field.send_keys(Keys.RETURN) @@ -72,10 +72,10 @@ class TestCase(ScreenshotTestCase): # Login of inactive user -> save error message self.user.is_active = False self.user.save() - username_field = c.find_element_by_id('id_username') + username_field = c.find_element(By.ID, 'id_username') username_field.clear() username_field.send_keys(self.test_username) - password_field = c.find_element_by_id('id_password') + password_field = c.find_element(By.ID, 'id_password') password_field.clear() password_field.send_keys(self.test_password) password_field.send_keys(Keys.RETURN) @@ -87,10 +87,10 @@ class TestCase(ScreenshotTestCase): self.user.save() # Login -> save success message - username_field = c.find_element_by_id('id_username') + username_field = c.find_element(By.ID, 'id_username') username_field.clear() username_field.send_keys(self.test_username) - password_field = c.find_element_by_id('id_password') + password_field = c.find_element(By.ID, 'id_password') password_field.clear() password_field.send_keys(self.test_password) password_field.send_keys(Keys.RETURN) @@ -104,18 +104,18 @@ class TestCase(ScreenshotTestCase): self.save_screenshot('user_menu', sequence=sequence_name) # Click on 'set password' -> save set password page - user_menu = c.find_element_by_css_selector('#login-widget ul') - link = user_menu.find_element_by_partial_link_text(ugettext('Passwort ändern')) + user_menu = c.find_element(By.CSS_SELECTOR, '#login-widget ul') + link = user_menu.find_element(By.PARTIAL_LINK_TEXT, ugettext('Passwort ändern')) link.click() password_field = self.wait_on_presence(c, (By.ID, 'id_new_password')) self.save_screenshot('empty_set_password_form', sequence=sequence_name) # Fill in a password -> Save dots in password field - send_mail_field = c.find_element_by_id('id_send_password_mail') + send_mail_field = c.find_element(By.ID, 'id_send_password_mail') send_mail_field.click() password_field.clear() password_field.send_keys(self.test_password) - password2_field = c.find_element_by_id('id_new_password_repeat') + password2_field = c.find_element(By.ID, 'id_new_password_repeat') password2_field.clear() password2_field.send_keys(self.test_password) self.save_screenshot('filled_set_password_form', sequence=sequence_name) @@ -131,10 +131,10 @@ class TestCase(ScreenshotTestCase): # New passwords too common and too short -> save error message password = 'abcdef' - password_field = c.find_element_by_id('id_new_password') + password_field = c.find_element(By.ID, 'id_new_password') password_field.clear() password_field.send_keys(password) - password2_field = c.find_element_by_id('id_new_password_repeat') + password2_field = c.find_element(By.ID, 'id_new_password_repeat') password2_field.clear() password2_field.send_keys(password) password2_field.send_keys(Keys.RETURN) @@ -143,10 +143,10 @@ class TestCase(ScreenshotTestCase): # New passwords entirely_numeric -> save error message password = '9126735804' - password_field = c.find_element_by_id('id_new_password') + password_field = c.find_element(By.ID, 'id_new_password') password_field.clear() password_field.send_keys(password) - password2_field = c.find_element_by_id('id_new_password_repeat') + password2_field = c.find_element(By.ID, 'id_new_password_repeat') password2_field.clear() password2_field.send_keys(password) password2_field.send_keys(Keys.RETURN) @@ -155,10 +155,10 @@ class TestCase(ScreenshotTestCase): # New passwords too similar -> save error message password = self.test_username - password_field = c.find_element_by_id('id_new_password') + password_field = c.find_element(By.ID, 'id_new_password') password_field.clear() password_field.send_keys(password) - password2_field = c.find_element_by_id('id_new_password_repeat') + password2_field = c.find_element(By.ID, 'id_new_password_repeat') password2_field.clear() password2_field.send_keys(password) password2_field.send_keys(Keys.RETURN) @@ -167,10 +167,10 @@ class TestCase(ScreenshotTestCase): # Change password -> save success message password = self.test_password[::-1] - password_field = c.find_element_by_id('id_new_password') + password_field = c.find_element(By.ID, 'id_new_password') password_field.clear() password_field.send_keys(password) - password2_field = c.find_element_by_id('id_new_password_repeat') + password2_field = c.find_element(By.ID, 'id_new_password_repeat') password2_field.clear() password2_field.send_keys(password) password2_field.send_keys(Keys.RETURN) @@ -179,7 +179,7 @@ class TestCase(ScreenshotTestCase): # Get password recreate page -> since we are logged in, it should # redirect to set password page again -> save - html = c.find_element_by_tag_name('html') + html = c.find_element(By.TAG_NAME, 'html') c.get(self.complete_url(reverse('dav_auth:recreate_password'))) self.wait_until_stale(c, html) self.wait_on_presence(c, (By.ID, 'id_new_password')) @@ -188,19 +188,19 @@ class TestCase(ScreenshotTestCase): # Click on 'logout' -> save page dropdown_button = self.wait_on_presence(c, (By.ID, 'user_dropdown_button')) dropdown_button.click() - user_menu = c.find_element_by_css_selector('#login-widget ul') - link = user_menu.find_element_by_partial_link_text(ugettext('Logout')) + user_menu = c.find_element(By.CSS_SELECTOR, '#login-widget ul') + link = user_menu.find_element(By.PARTIAL_LINK_TEXT, ugettext('Logout')) link.click() self.wait_until_stale(c, user_menu) self.save_screenshot('logout_succeed', sequence=sequence_name) # Click on 'login' to access password recreate link - link = c.find_element_by_css_selector('#login-widget a') + link = c.find_element(By.CSS_SELECTOR, '#login-widget a') link.click() self.wait_on_presence(c, (By.ID, 'id_username')) # Locate password recreate link, click it -> save password recreate form - link = c.find_element_by_partial_link_text(ugettext('Passwort vergessen')) + link = c.find_element(By.PARTIAL_LINK_TEXT, ugettext('Passwort vergessen')) link.click() username_field = self.wait_on_presence(c, (By.ID, 'id_username')) self.save_screenshot('empty_recreate_password_form', sequence=sequence_name) @@ -212,7 +212,7 @@ class TestCase(ScreenshotTestCase): self.save_screenshot('recreate_password_invalid_user', sequence=sequence_name) # Locate password recreate link, click it - link = c.find_element_by_partial_link_text(ugettext('Passwort vergessen')) + link = c.find_element(By.PARTIAL_LINK_TEXT, ugettext('Passwort vergessen')) link.click() username_field = self.wait_on_presence(c, (By.ID, 'id_username')) diff --git a/dav_auth/tests/test_templates.py b/dav_auth/tests/test_templates.py index 211d7c0..b33ca12 100644 --- a/dav_auth/tests/test_templates.py +++ b/dav_auth/tests/test_templates.py @@ -40,7 +40,7 @@ class TestCase(SeleniumAuthMixin, SeleniumTestCase): c = self.selenium c.get(self.complete_url('/')) try: - c.find_element_by_css_selector('#login-widget a') + c.find_element(By.CSS_SELECTOR, '#login-widget a') except NoSuchElementException as e: # pragma: no cover self.fail(str(e)) @@ -50,9 +50,9 @@ class TestCase(SeleniumAuthMixin, SeleniumTestCase): location = reverse('dav_auth:login') c.get(self.complete_url(location)) - field = c.find_element_by_id('id_username') + field = c.find_element(By.ID, 'id_username') self.assertEqual(field.get_attribute('required'), 'true') - field = c.find_element_by_id('id_password') + field = c.find_element(By.ID, 'id_password') self.assertEqual(field.get_attribute('required'), 'true') def test_required_fields_in_set_password_form(self): @@ -61,7 +61,7 @@ class TestCase(SeleniumAuthMixin, SeleniumTestCase): c.get(self.complete_url(reverse('dav_auth:set_password'))) field = self.wait_on_presence(c, (By.ID, 'id_new_password')) self.assertEqual(field.get_attribute('required'), 'true') - field = c.find_element_by_id('id_new_password_repeat') + field = c.find_element(By.ID, 'id_new_password_repeat') self.assertEqual(field.get_attribute('required'), 'true') - field = c.find_element_by_id('id_send_password_mail') + field = c.find_element(By.ID, 'id_send_password_mail') self.assertEqual(field.get_attribute('required'), None) diff --git a/dav_events/tests/test_screenshots.py b/dav_events/tests/test_screenshots.py index 78de5aa..40d7668 100644 --- a/dav_events/tests/test_screenshots.py +++ b/dav_events/tests/test_screenshots.py @@ -135,30 +135,30 @@ class TestCase(SeleniumAuthMixin, RoleMixin, ScreenshotTestCase): self.save_screenshot('mode_form', sequence=sequence_name) if 'mode' in data: - field = c.find_element_by_id('id_mode') - radio = field.find_element_by_css_selector('input[value=\'{}\']'.format(data['mode'])) + field = c.find_element(By.ID, 'id_mode') + radio = field.find_element(By.CSS_SELECTOR, 'input[value=\'{}\']'.format(data['mode'])) radio.click() if 'sport' in data: - field = c.find_element_by_id('id_sport') - radio = field.find_element_by_css_selector('input[value=\'{}\']'.format(data['sport'])) + field = c.find_element(By.ID, 'id_sport') + radio = field.find_element(By.CSS_SELECTOR, 'input[value=\'{}\']'.format(data['sport'])) radio.click() if 'level' in data: - field = c.find_element_by_id('id_level') - radio = field.find_element_by_css_selector('input[value=\'{}\']'.format(data['level'])) + field = c.find_element(By.ID, 'id_level') + radio = field.find_element(By.CSS_SELECTOR, 'input[value=\'{}\']'.format(data['level'])) radio.click() if 'ski_lift' in data and data['ski_lift']: - field = c.find_element_by_id('id_ski_lift') + field = c.find_element(By.ID, 'id_ski_lift') field.click() - field = c.find_element_by_css_selector('input#id_first_day_widget') + field = c.find_element(By.CSS_SELECTOR, 'input#id_first_day_widget') field.send_keys(data['first_day']) - td = c.find_element_by_css_selector('div.datetimepicker-days td.active') + td = c.find_element(By.CSS_SELECTOR, 'div.datetimepicker-days td.active') td.click() - field = c.find_element_by_css_selector('input#id_last_day_widget') + field = c.find_element(By.CSS_SELECTOR, 'input#id_last_day_widget') field.click() if screenshots: self.save_screenshot('last_date_widget', sequence=sequence_name) @@ -166,74 +166,74 @@ class TestCase(SeleniumAuthMixin, RoleMixin, ScreenshotTestCase): field.send_keys(data['last_day']) if 'alt_first_day' in data: - field = c.find_element_by_css_selector('input#id_alt_first_day_widget') + field = c.find_element(By.CSS_SELECTOR, 'input#id_alt_first_day_widget') field.send_keys(data['alt_first_day']) if 'alt_last_day' in data: - field = c.find_element_by_css_selector('input#id_alt_last_day_widget') + field = c.find_element(By.CSS_SELECTOR, 'input#id_alt_last_day_widget') field.send_keys(data['alt_last_day']) - button = c.find_element_by_id('btn-form-next') + button = c.find_element(By.ID, 'btn-form-next') button.click() self.wait_until_stale(c, button) if screenshots: self.save_screenshot('location_form', sequence=sequence_name) if 'country' in data: - field = c.find_element_by_id('id_country') + field = c.find_element(By.ID, 'id_country') Select(field).select_by_value(data['country']) if 'terrain' in data: - field = c.find_element_by_id('id_terrain') + field = c.find_element(By.ID, 'id_terrain') Select(field).select_by_value(data['terrain']) if 'location' in data: - field = c.find_element_by_id('id_location') + field = c.find_element(By.ID, 'id_location') field.send_keys(data['location']) if 'transport_other' in data: - field = c.find_element_by_id('id_transport_other') + field = c.find_element(By.ID, 'id_transport_other') field.send_keys(data['transport_other']) if 'transport' in data: - field = c.find_element_by_id('id_transport') + field = c.find_element(By.ID, 'id_transport') Select(field).select_by_value(data['transport']) - button = c.find_element_by_id('btn-form-next') + button = c.find_element(By.ID, 'btn-form-next') button.click() self.wait_until_stale(c, button) if screenshots: self.save_screenshot('journey_form', sequence=sequence_name) if 'meeting_point_other' in data: - field = c.find_element_by_id('id_meeting_point_other') + field = c.find_element(By.ID, 'id_meeting_point_other') field.send_keys(data['meeting_point_other']) if 'meeting_point' in data: - field = c.find_element_by_id('id_meeting_point') + field = c.find_element(By.ID, 'id_meeting_point') Select(field).select_by_value(data['meeting_point']) if 'meeting_time' in data: - field = c.find_element_by_css_selector('input#id_meeting_time_widget') + field = c.find_element(By.CSS_SELECTOR, 'input#id_meeting_time_widget') field.send_keys(data['meeting_time']) if 'departure_time' in data: - field = c.find_element_by_css_selector('input#id_departure_time_widget') + field = c.find_element(By.CSS_SELECTOR, 'input#id_departure_time_widget') field.send_keys(data['departure_time']) if 'departure_ride' in data: - field = c.find_element_by_id('id_departure_ride') + field = c.find_element(By.ID, 'id_departure_ride') field.send_keys(data['departure_ride']) if 'return_departure_time' in data: - field = c.find_element_by_css_selector('input#id_return_departure_time_widget') + field = c.find_element(By.CSS_SELECTOR, 'input#id_return_departure_time_widget') field.send_keys(data['return_departure_time']) if 'return_arrival_time' in data: - field = c.find_element_by_css_selector('input#id_return_arrival_time_widget') + field = c.find_element(By.CSS_SELECTOR, 'input#id_return_arrival_time_widget') field.send_keys(data['return_arrival_time']) if 'arrival_previous_day' in data and data['arrival_previous_day']: - field = c.find_element_by_id('id_arrival_previous_day') + field = c.find_element(By.ID, 'id_arrival_previous_day') field.click() - button = c.find_element_by_id('btn-form-next') + button = c.find_element(By.ID, 'btn-form-next') button.click() self.wait_until_stale(c, button) @@ -242,18 +242,18 @@ class TestCase(SeleniumAuthMixin, RoleMixin, ScreenshotTestCase): self.save_screenshot('accommodation_form', sequence=sequence_name) if 'basecamp' in data: - field = c.find_element_by_id('id_basecamp') + field = c.find_element(By.ID, 'id_basecamp') field.send_keys(data['basecamp']) if 'accommodation' in data: - field = c.find_element_by_id('id_accommodation') + field = c.find_element(By.ID, 'id_accommodation') Select(field).select_by_value(data['accommodation']) if 'meals_other' in data: - field = c.find_element_by_id('id_meals_other') + field = c.find_element(By.ID, 'id_meals_other') field.send_keys(data['meals_other']) - button = c.find_element_by_id('btn-form-next') + button = c.find_element(By.ID, 'btn-form-next') button.click() self.wait_until_stale(c, button) @@ -261,7 +261,7 @@ class TestCase(SeleniumAuthMixin, RoleMixin, ScreenshotTestCase): self.save_screenshot('requirements_form', sequence=sequence_name) if 'requirements_add' in data: - field = c.find_element_by_id('id_requirements') + field = c.find_element(By.ID, 'id_requirements') field.send_keys(Keys.RETURN) if isinstance(data['requirements_add'], list): field.send_keys(Keys.RETURN.join(data['requirements_add'])) @@ -269,14 +269,14 @@ class TestCase(SeleniumAuthMixin, RoleMixin, ScreenshotTestCase): field.send_keys(data['requirements_add']) if 'pre_meeting_1' in data: - field = c.find_element_by_css_selector('input#id_pre_meeting_1_widget') + field = c.find_element(By.CSS_SELECTOR, 'input#id_pre_meeting_1_widget') field.send_keys(data['pre_meeting_1']) if 'pre_meeting_2' in data: - field = c.find_element_by_css_selector('input#id_pre_meeting_2_widget') + field = c.find_element(By.CSS_SELECTOR, 'input#id_pre_meeting_2_widget') field.send_keys(data['pre_meeting_2']) - button = c.find_element_by_id('btn-form-next') + button = c.find_element(By.ID, 'btn-form-next') button.click() self.wait_until_stale(c, button) if screenshots: @@ -289,60 +289,60 @@ class TestCase(SeleniumAuthMixin, RoleMixin, ScreenshotTestCase): field.send_keys(data['trainer_firstname']) if 'trainer_familyname' in data: - field = c.find_element_by_id('id_trainer_familyname') + field = c.find_element(By.ID, 'id_trainer_familyname') if auth: field.clear() field.send_keys(data['trainer_familyname']) if 'trainer_email' in data: - field = c.find_element_by_id('id_trainer_email') + field = c.find_element(By.ID, 'id_trainer_email') if auth: field.clear() field.send_keys(data['trainer_email']) if 'trainer_phone' in data: - field = c.find_element_by_id('id_trainer_phone') + field = c.find_element(By.ID, 'id_trainer_phone') if auth: field.clear() field.send_keys(data['trainer_phone']) if 'trainer_2_fullname' in data: - field = c.find_element_by_id('id_trainer_2_fullname') + field = c.find_element(By.ID, 'id_trainer_2_fullname') field.send_keys(data['trainer_2_fullname']) if 'trainer_3_fullname' in data: - field = c.find_element_by_id('id_trainer_3_fullname') + field = c.find_element(By.ID, 'id_trainer_3_fullname') field.send_keys(data['trainer_3_fullname']) - button = c.find_element_by_id('btn-form-next') + button = c.find_element(By.ID, 'btn-form-next') button.click() self.wait_until_stale(c, button) if screenshots: self.save_screenshot('registration_form', sequence=sequence_name) if 'deadline' in data: - field = c.find_element_by_id('id_deadline') - radio = field.find_element_by_css_selector('input[value=\'{}\']'.format(data['deadline'])) + field = c.find_element(By.ID, 'id_deadline') + radio = field.find_element(By.CSS_SELECTOR, 'input[value=\'{}\']'.format(data['deadline'])) radio.click() - button = c.find_element_by_id('btn-form-next') + button = c.find_element(By.ID, 'btn-form-next') button.click() self.wait_until_stale(c, button) if screenshots: self.save_screenshot('charges_form', sequence=sequence_name) if 'charge' in data: - field = c.find_element_by_id('id_charge') + field = c.find_element(By.ID, 'id_charge') field.clear() field.send_keys(data['charge']) if 'additional_costs' in data: - field = c.find_element_by_id('id_additional_costs') + field = c.find_element(By.ID, 'id_additional_costs') field.clear() if data['additional_costs'] is not None: field.send_keys(data['additional_costs']) - button = c.find_element_by_id('btn-form-next') + button = c.find_element(By.ID, 'btn-form-next') button.click() self.wait_until_stale(c, button) @@ -350,49 +350,49 @@ class TestCase(SeleniumAuthMixin, RoleMixin, ScreenshotTestCase): if screenshots: self.save_screenshot('training_form', sequence=sequence_name) - field = c.find_element_by_id('id_course_topic_1') + field = c.find_element(By.ID, 'id_course_topic_1') if isinstance(data['course_topic_1'], list): field.send_keys(Keys.RETURN.join(data['course_topic_1'])) else: field.send_keys(data['course_topic_1']) if 'course_topic_2' in data: - field = c.find_element_by_id('id_course_topic_2') + field = c.find_element(By.ID, 'id_course_topic_2') field.send_keys(data['course_topic_2']) if 'course_topic_3' in data: - button = c.find_element_by_id('btn-topic-add') + button = c.find_element(By.ID, 'btn-topic-add') button.click() - field = c.find_element_by_id('id_course_topic_3') + field = c.find_element(By.ID, 'id_course_topic_3') field.send_keys(data['course_topic_3']) if 'course_topic_4' in data: button.click() - field = c.find_element_by_id('id_course_topic_4') + field = c.find_element(By.ID, 'id_course_topic_4') field.send_keys(data['course_topic_4']) - field = c.find_element_by_id('id_course_goal_1') + field = c.find_element(By.ID, 'id_course_goal_1') if isinstance(data['course_goal_1'], list): field.send_keys(Keys.RETURN.join(data['course_goal_1'])) else: field.send_keys(data['course_goal_1']) if 'course_goal_2' in data: - field = c.find_element_by_id('id_course_goal_2') + field = c.find_element(By.ID, 'id_course_goal_2') field.send_keys(data['course_goal_2']) if 'course_goal_3' in data: - button = c.find_element_by_id('btn-goal-add') + button = c.find_element(By.ID, 'btn-goal-add') button.click() - field = c.find_element_by_id('id_course_goal_3') + field = c.find_element(By.ID, 'id_course_goal_3') field.send_keys(data['course_goal_3']) if 'course_goal_4' in data: button.click() - field = c.find_element_by_id('id_course_goal_4') + field = c.find_element(By.ID, 'id_course_goal_4') field.send_keys(data['course_goal_4']) - button = c.find_element_by_id('btn-form-next') + button = c.find_element(By.ID, 'btn-form-next') button.click() self.wait_until_stale(c, button) @@ -400,29 +400,29 @@ class TestCase(SeleniumAuthMixin, RoleMixin, ScreenshotTestCase): self.save_screenshot('description_form', sequence=sequence_name) if 'title' in data: - field = c.find_element_by_id('id_title') + field = c.find_element(By.ID, 'id_title') field.clear() field.send_keys(data['title']) - field = c.find_element_by_id('id_description') + field = c.find_element(By.ID, 'id_description') if isinstance(data['description'], list): field.send_keys(Keys.RETURN.join(data['description'])) else: field.send_keys(data['description']) - button = c.find_element_by_id('btn-form-next') + button = c.find_element(By.ID, 'btn-form-next') button.click() self.wait_until_stale(c, button) if screenshots: self.save_screenshot('summary_form', sequence=sequence_name) - button = c.find_element_by_id('btn-form-back') + button = c.find_element(By.ID, 'btn-form-back') button.click() self.wait_until_stale(c, button) for i in range(0, 11): try: - button = c.find_element_by_id('btn-form-next') + button = c.find_element(By.ID, 'btn-form-next') button.click() self.wait_until_stale(c, button) except NoSuchElementException: @@ -431,13 +431,13 @@ class TestCase(SeleniumAuthMixin, RoleMixin, ScreenshotTestCase): self.fail('Too many sub forms') if 'internal_note' in data: - field = c.find_element_by_id('id_internal_note') + field = c.find_element(By.ID, 'id_internal_note') if isinstance(data['internal_note'], list): field.send_keys(Keys.RETURN.join(data['internal_note'])) else: field.send_keys(data['internal_note']) - button = c.find_element_by_id('btn-form-submit') + button = c.find_element(By.ID, 'btn-form-submit') button.click() self.wait_until_stale(c, button) @@ -445,9 +445,9 @@ class TestCase(SeleniumAuthMixin, RoleMixin, ScreenshotTestCase): if screenshots: self.save_screenshot('user_and_event_created_set_password', sequence=sequence_name) - field = c.find_element_by_id('id_new_password') + field = c.find_element(By.ID, 'id_new_password') field.send_keys(TEST_PASSWORD) - field = c.find_element_by_id('id_new_password_repeat') + field = c.find_element(By.ID, 'id_new_password_repeat') field.send_keys(TEST_PASSWORD) field.send_keys(Keys.RETURN) @@ -471,20 +471,20 @@ class TestCase(SeleniumAuthMixin, RoleMixin, ScreenshotTestCase): if screenshots: self.save_screenshot('event_list_before', sequence=sequence_name) - link = c.find_element_by_link_text(title) + link = c.find_element(By.LINK_TEXT, title) link.click() self.wait_until_stale(c, link) if screenshots: self.save_screenshot('event_details', sequence=sequence_name) - button = c.find_element_by_id('btn-clone') + button = c.find_element(By.ID, 'btn-clone') button.click() self.wait_until_stale(c, button) for i in range(0, 11): try: - button = c.find_element_by_id('btn-form-next') + button = c.find_element(By.ID, 'btn-form-next') except NoSuchElementException: break @@ -492,8 +492,8 @@ class TestCase(SeleniumAuthMixin, RoleMixin, ScreenshotTestCase): self.save_screenshot('form', sequence=sequence_name) try: - field = c.find_element_by_id('id_deadline') - radio = field.find_element_by_css_selector('input[value=\'OTHER\']') + field = c.find_element(By.ID, 'id_deadline') + radio = field.find_element(By.CSS_SELECTOR, 'input[value=\'OTHER\']') radio.click() except NoSuchElementException: pass @@ -506,7 +506,7 @@ class TestCase(SeleniumAuthMixin, RoleMixin, ScreenshotTestCase): if screenshots: self.save_screenshot('summary', sequence=sequence_name) - button = c.find_element_by_id('btn-form-submit') + button = c.find_element(By.ID, 'btn-form-submit') button.click() self.wait_until_stale(c, button) @@ -523,28 +523,28 @@ class TestCase(SeleniumAuthMixin, RoleMixin, ScreenshotTestCase): button.click() self.wait_until_stale(c, button) - link = c.find_element_by_link_text(title) + link = c.find_element(By.LINK_TEXT, title) link.click() self.wait_until_stale(c, link) - action_tabs = c.find_element_by_css_selector('.action-tabs > ul.nav-tabs') - tab = action_tabs.find_element_by_link_text(ugettext(u'Ändern')) + action_tabs = c.find_element(By.CSS_SELECTOR, '.action-tabs > ul.nav-tabs') + tab = action_tabs.find_element(By.LINK_TEXT, ugettext(u'Ändern')) tab.click() self.wait_until_stale(c, tab) - panels = c.find_elements_by_css_selector('#form-accordion .panel-collapse') + panels = c.find_elements(By.CSS_SELECTOR, '#form-accordion .panel-collapse') for panel in panels[:-1]: if screenshots: self.save_screenshot('edit-form', sequence=sequence_name) - button = panel.find_element_by_partial_link_text(ugettext(u'Nächster Abschnitt')) + button = panel.find_element(By.PARTIAL_LINK_TEXT, ugettext(u'Nächster Abschnitt')) button.click() time.sleep(.5) if screenshots: self.save_screenshot('edit-form', sequence=sequence_name) - button = c.find_element_by_css_selector('form button[type="submit"]') + button = c.find_element(By.CSS_SELECTOR, 'form button[type="submit"]') button.click() self.wait_until_stale(c, button) @@ -561,26 +561,26 @@ class TestCase(SeleniumAuthMixin, RoleMixin, ScreenshotTestCase): if screenshots: self.save_screenshot('event_list_before', sequence=sequence_name) - link = c.find_element_by_link_text(title) + link = c.find_element(By.LINK_TEXT, title) link.click() self.wait_until_stale(c, link) if screenshots: self.save_screenshot('event_details', sequence=sequence_name) - button = c.find_element_by_id('btn-accept') + button = c.find_element(By.ID, 'btn-accept') button.click() time.sleep(.5) if screenshots: self.save_screenshot('accept_modal', sequence=sequence_name) - button = c.find_element_by_css_selector('#modal-accept-dialog a.btn-success') + button = c.find_element(By.CSS_SELECTOR, '#modal-accept-dialog a.btn-success') button.click() self.wait_until_stale(c, button) if screenshots: self.save_screenshot('accepted', sequence=sequence_name) - link = c.find_element_by_link_text(ugettext('Veranstaltungsliste')) + link = c.find_element(By.LINK_TEXT, ugettext('Veranstaltungsliste')) link.click() self.wait_until_stale(c, link) if screenshots: @@ -599,26 +599,26 @@ class TestCase(SeleniumAuthMixin, RoleMixin, ScreenshotTestCase): if screenshots: self.save_screenshot('event_list_before', sequence=sequence_name) - link = c.find_element_by_link_text(title) + link = c.find_element(By.LINK_TEXT, title) link.click() self.wait_until_stale(c, link) if screenshots: self.save_screenshot('event_details', sequence=sequence_name) - button = c.find_element_by_id('btn-confirmpublication') + button = c.find_element(By.ID, 'btn-confirmpublication') button.click() time.sleep(.5) if screenshots: self.save_screenshot('confirmpublication_modal', sequence=sequence_name) - button = c.find_element_by_css_selector('#btn-confirmpublication-{}'.format(channel)) + button = c.find_element(By.CSS_SELECTOR, '#btn-confirmpublication-{}'.format(channel)) button.click() self.wait_until_stale(c, button) if screenshots: self.save_screenshot('confirmed_{}'.format(channel), sequence=sequence_name) - link = c.find_element_by_link_text(ugettext('Veranstaltungsliste')) + link = c.find_element(By.LINK_TEXT, ugettext('Veranstaltungsliste')) link.click() self.wait_until_stale(c, link) if screenshots: @@ -637,20 +637,20 @@ class TestCase(SeleniumAuthMixin, RoleMixin, ScreenshotTestCase): if screenshots: self.save_screenshot('event_list_before', sequence=sequence_name) - link = c.find_element_by_link_text(title) + link = c.find_element(By.LINK_TEXT, title) link.click() self.wait_until_stale(c, link) if screenshots: self.save_screenshot('event_details', sequence=sequence_name) - button = c.find_element_by_id('btn-confirmclearance') + button = c.find_element(By.ID, 'btn-confirmclearance') button.click() self.wait_until_stale(c, button) if screenshots: self.save_screenshot('confirmed_clearance', sequence=sequence_name) - link = c.find_element_by_link_text(ugettext('Veranstaltungsliste')) + link = c.find_element(By.LINK_TEXT, ugettext('Veranstaltungsliste')) link.click() self.wait_until_stale(c, link) if screenshots: @@ -688,7 +688,7 @@ class TestCase(SeleniumAuthMixin, RoleMixin, ScreenshotTestCase): self.create_event(TEST_EVENT_DATA_W, auth=True) self.create_event(TEST_EVENT_DATA_M, auth=True) - link = c.find_element_by_link_text(TEST_EVENT_DATA_W['title']) + link = c.find_element(By.LINK_TEXT, TEST_EVENT_DATA_W['title']) link.click() self.wait_until_stale(c, link) self.save_screenshot('owner_event_details') @@ -730,7 +730,7 @@ class TestCase(SeleniumAuthMixin, RoleMixin, ScreenshotTestCase): button.click() self.wait_until_stale(c, button) - link = c.find_element_by_partial_link_text(ugettext(u'Veranstaltungsliste herunterladen')) + link = c.find_element(By.PARTIAL_LINK_TEXT, ugettext(u'Veranstaltungsliste herunterladen')) link.click() self.wait_until_stale(c, link) self.save_screenshot('event_export_form') diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..d457057 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,6 @@ +babel +django<3.3 +django-bootstrap3 +django-countries +django-datetime-widget2 +setuptools diff --git a/setup.py b/setup.py index dd31e5a..3ba0d06 100644 --- a/setup.py +++ b/setup.py @@ -6,17 +6,7 @@ from setuptools import setup, find_packages from setuptools import Command -class MyCommand(Command): - user_options = [] - - def initialize_options(self): - pass - - def finalize_options(self): - pass - - -class SetupPythonEnvironment(MyCommand): +class SetupPythonEnvironment(Command): description = 'create a (virtual) python environment' def run(self): @@ -54,7 +44,7 @@ class SetupPythonEnvironment(MyCommand): print('- All others: source %s/bin/activate' % path) -class QuickSetup(MyCommand): +class QuickSetup(Command): description = 'create a typical installation for developing' def run(self): @@ -92,10 +82,19 @@ class QuickSetup(MyCommand): os.system(cmd) +def get_long_description(): + path = os.path.abspath(os.path.dirname(__file__)) + file = os.path.join(path, 'README.rst') + with open(file) as f: + return f.read() + + setup( name='django-dav-events', - version='2.1.2', - description='A django based web application project to organize DAV Events.', + version='2.2.1', + description='A django based web application project to drive the Touren- & Kurseportal of DAV Karlsruhe.', + long_description=get_long_description(), + long_description_content_type='text/x-rst', url='https://dev.heinzelwerk.de/git/DAV-KA/django-dav-events', author='Jens Kleineheismann', author_email='heinzel@alpenverein-karlsruhe.de', @@ -105,26 +104,9 @@ setup( }, packages=find_packages(), include_package_data=True, - test_suite='tests.test_suite', entry_points={ 'console_scripts': [ 'django-dav-admin = dav_base.console_scripts.admin:main', ], }, - install_requires=[ - 'babel', - #'django >= 1.11, < 2.0', - 'django >= 1.11, < 3.3', - # 'django-extensions', - 'django-bootstrap3', - 'django-countries', - 'django-datetime-widget2', - 'pytz', - 'selenium', - 'setuptools', - 'coverage', - ], - extras_require={ - 'production': ['psycopg2'], - }, ) diff --git a/tox.ini b/tox.ini index a04e348..510b9e5 100644 --- a/tox.ini +++ b/tox.ini @@ -1,10 +1,29 @@ [tox] -envlist = py311, py312 +envlist = fresh, coverage [testenv] -recreate = false setenv = PYTHONPATH = . -commands = python --version - python -m coverage run tests - coverage report --skip-covered +deps = -r{toxinidir}/requirements.txt + selenium +commands_pre = python --version + python -m django --version +commands = python tests + +[testenv:fast] +description = Only run the testsuite + +[testenv:fresh] +description = Run tests in freshly created environment (with coverage) +recreate = true +deps = {[testenv]deps} + coverage +commands_pre = {[testenv]commands_pre} + python -m coverage --version +commands = python -m coverage run tests + +[testenv:coverage] +description = Report test coverage +deps = {[testenv:fresh]deps} +commands = python -m coverage report --skip-covered +