├── core ├── __init__.py ├── migrations │ ├── __init__.py │ ├── 0002_category_slug.py │ └── 0001_initial.py ├── templatetags │ ├── __init__.py │ └── makeform.py ├── utils.py ├── models.py ├── context_processors.py ├── middleware.py └── fixtures │ └── default_choices.json ├── mos ├── __init__.py ├── settings │ ├── __init__.py │ ├── devel.py │ ├── deploy.py.tpl │ ├── deploy_env.py │ └── common.py ├── asgi.py └── urls.py ├── things ├── __init__.py ├── migrations │ ├── __init__.py │ ├── 0004_remove_thing_name.py │ ├── 0007_alter_thingevent_usage_seconds.py │ ├── 0009_thing_expiry_notice.py │ ├── 0008_alter_thingevent_kind.py │ ├── 0002_auto_20240224_1354.py │ ├── 0005_auto_20240224_1447.py │ ├── 0003_auto_20240224_1415.py │ ├── 0001_initial.py │ └── 0006_thingevent.py ├── apps.py ├── urls.py ├── management │ └── commands │ │ └── send_thing_expiration_notices.py ├── admin.py ├── models.py └── views.py ├── web ├── __init__.py ├── tests.py └── views.py ├── announce ├── __init__.py ├── urls.py ├── templates │ └── announce │ │ ├── message_sent.html │ │ └── write_message.html └── views.py ├── members ├── __init__.py ├── tests │ ├── __init__.py │ ├── test_contact_info.py │ └── test_membership_period.py ├── management │ ├── __init__.py │ └── commands │ │ ├── __init__.py │ │ ├── domail.py │ │ ├── list_intern_emails.py │ │ ├── member_categories.py │ │ ├── generate_many_members.py │ │ └── invite_matrix_users.py ├── migrations │ ├── __init__.py │ ├── 0017_merge_20240224_1514.py │ ├── 0024_alter_kindofmembership_options.py │ ├── 0006_alter_contactinfo_last_email_ok.py │ ├── 0009_pendingpayment_original_file.py │ ├── 0016_membershipperiod_comment.py │ ├── 0013_alter_bankimportmatcher_comment.py │ ├── 0023_contactinfo_gdpr_wiped_on.py │ ├── 0016_auto_20240224_1348.py │ ├── 0018_remove_paymentinfo_bank_account_number_and_more.py │ ├── 0002_auto_20221112_1256.py │ ├── 0007_alter_contactinfo_image.py │ ├── 0010_alter_paymentinfo_bank_account_iban.py │ ├── 0005_mailinglistmail.py │ ├── 0021_communicationrecord_monthly_fee_at_contact_and_more.py │ ├── 0022_contactinfo_in_intern_matrix_room_and_more.py │ ├── 0025_alter_contactinfo_matrix_handle.py │ ├── 0019_pendingpayment_creator.py │ ├── 0012_bankimportmatcher.py │ ├── 0003_auto_20221112_1308.py │ ├── 0014_auto_20230709_2019.py │ ├── 0004_auto_20221112_1422.py │ ├── 0011_locker.py │ ├── 0015_auto_20230709_2031.py │ ├── 0008_pendingpayment.py │ └── 0020_communicationrecord.py ├── templates │ └── admin │ │ ├── change_form.html │ │ └── wipe_members.html ├── middleware.py ├── forms.py ├── urls.py └── util.py ├── projects ├── __init__.py ├── migrations │ ├── __init__.py │ └── 0001_initial.py ├── admin.py ├── templates │ └── projects │ │ ├── project_archive.html │ │ ├── project_detail.html │ │ ├── overview.inc │ │ └── projectinfo.inc ├── forms.py ├── models.py ├── urls.py └── views.py ├── sources ├── __init__.py ├── tests │ ├── __init__.py │ └── test_cronjob.py ├── management │ ├── __init__.py │ └── commands │ │ ├── __init__.py │ │ ├── get_wiki_changes.py │ │ └── jour_fixe_reminder.py ├── migrations │ ├── __init__.py │ └── 0001_initial.py └── models.py ├── cal ├── migrations │ ├── __init__.py │ ├── 0002_event_advertise.py │ ├── 0003_alter_event_advertise.py │ ├── 0004_event_wikiimagepage.py │ └── 0001_initial.py ├── templatetags │ ├── __init__.py │ └── cal_tags.py ├── admin.py ├── __init__.py ├── fixtures │ └── initial_user.json ├── feeds.py ├── forms.py └── urls.py ├── static ├── stylesheets │ ├── monobook.css │ ├── metanight.png │ ├── base.css │ ├── projects.css │ ├── members.css │ ├── reset.css │ ├── soup_startpage.css │ ├── cellardoor.css │ ├── global.css │ └── layout.css ├── images │ ├── bg.gif │ ├── fail.jpg │ ├── logo.gif │ ├── logo.png │ ├── pixel.gif │ ├── Flag_UK.png │ ├── atomic.gif │ ├── pixel_blue.png │ ├── soup_logo.png │ ├── logo_trauer.png │ ├── metasense_on.gif │ ├── default_avatar.png │ └── metasense_off.gif └── javascripts │ ├── sorttable.js │ ├── formset_handler.js │ └── ml-ajaxtools.js ├── templates ├── 404.html ├── members │ ├── contact_info.inc │ ├── new_member_welcome.mail.subject │ ├── members_details_error.inc │ ├── member_update_userpic.html │ ├── member_bank.html │ ├── members_details.html │ ├── member_list.html │ ├── members_list.html │ ├── member_list.inc │ ├── members_history.html │ ├── member_list_superuser.inc │ ├── members_hetti.html │ └── member_bank_json_match.html ├── things │ ├── thing_expiration_notice.mail.subject │ └── thing_expiration_notice.mail ├── rss │ ├── change_archive.html │ └── recentchanges.inc ├── 500.html ├── jour_fixe_reminder.mail.subject ├── registration │ ├── logged_out.html │ ├── password_change_done.html │ ├── login.html │ └── password_change_form.html ├── auth │ ├── user_list_mainpage.inc │ ├── user_list.html │ ├── user_list.inc │ └── user_list_superuser.inc ├── cal │ ├── event_detail.html │ ├── calendar.inc │ ├── event_archive.html │ ├── event_archive_year.html │ ├── eventinfo_nf.inc │ ├── eventinfo_detail.inc │ └── event_form.inc ├── welcome.mail ├── jour_fixe_reminder.mail ├── cellardoor.html ├── index.html └── base.html ├── .dockerignore ├── requirements-dev.txt ├── .gitignore ├── requirements.txt ├── HACKING_WITH_VAGRANT ├── HACKING_WITH_DOCKER ├── bootstrap_ansible.sh ├── .pre-commit-config.yaml ├── manage.py ├── docker ├── Dockerfile ├── entrypoint.sh └── docker-compose.yml ├── README.rst ├── docs └── THINGS.md ├── wsgi.py ├── HACKING ├── Vagrantfile └── provision_vagrant.yml /core/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /mos/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /things/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /web/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /announce/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /members/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /projects/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /sources/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /cal/migrations/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /cal/templatetags/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /core/migrations/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /members/tests/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /mos/settings/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /sources/tests/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /core/templatetags/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /members/management/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /members/migrations/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /projects/migrations/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /sources/management/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /sources/migrations/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /static/stylesheets/monobook.css: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /templates/404.html: -------------------------------------------------------------------------------- 1 | ohai 404! 2 | -------------------------------------------------------------------------------- /things/migrations/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.dockerignore: -------------------------------------------------------------------------------- 1 | /docker/volume-* 2 | -------------------------------------------------------------------------------- /templates/members/contact_info.inc: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /members/management/commands/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /sources/management/commands/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /templates/members/new_member_welcome.mail.subject: -------------------------------------------------------------------------------- 1 | Willkommen im Metalab 2 | -------------------------------------------------------------------------------- /static/images/bg.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Metalab/mos/HEAD/static/images/bg.gif -------------------------------------------------------------------------------- /static/images/fail.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Metalab/mos/HEAD/static/images/fail.jpg -------------------------------------------------------------------------------- /static/images/logo.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Metalab/mos/HEAD/static/images/logo.gif -------------------------------------------------------------------------------- /static/images/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Metalab/mos/HEAD/static/images/logo.png -------------------------------------------------------------------------------- /static/images/pixel.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Metalab/mos/HEAD/static/images/pixel.gif -------------------------------------------------------------------------------- /static/images/Flag_UK.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Metalab/mos/HEAD/static/images/Flag_UK.png -------------------------------------------------------------------------------- /static/images/atomic.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Metalab/mos/HEAD/static/images/atomic.gif -------------------------------------------------------------------------------- /templates/members/members_details_error.inc: -------------------------------------------------------------------------------- 1 | you are not allowed to see the profile of the user 2 | -------------------------------------------------------------------------------- /static/images/pixel_blue.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Metalab/mos/HEAD/static/images/pixel_blue.png -------------------------------------------------------------------------------- /static/images/soup_logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Metalab/mos/HEAD/static/images/soup_logo.png -------------------------------------------------------------------------------- /static/images/logo_trauer.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Metalab/mos/HEAD/static/images/logo_trauer.png -------------------------------------------------------------------------------- /static/images/metasense_on.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Metalab/mos/HEAD/static/images/metasense_on.gif -------------------------------------------------------------------------------- /static/images/default_avatar.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Metalab/mos/HEAD/static/images/default_avatar.png -------------------------------------------------------------------------------- /static/images/metasense_off.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Metalab/mos/HEAD/static/images/metasense_off.gif -------------------------------------------------------------------------------- /static/javascripts/sorttable.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Metalab/mos/HEAD/static/javascripts/sorttable.js -------------------------------------------------------------------------------- /static/stylesheets/metanight.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Metalab/mos/HEAD/static/stylesheets/metanight.png -------------------------------------------------------------------------------- /templates/things/thing_expiration_notice.mail.subject: -------------------------------------------------------------------------------- 1 | ACHTUNG: Dein metalab Zugang: {{thing}} läuft bald ab! 2 | -------------------------------------------------------------------------------- /projects/admin.py: -------------------------------------------------------------------------------- 1 | from django.contrib import admin 2 | 3 | from .models import Project 4 | 5 | admin.site.register(Project) 6 | -------------------------------------------------------------------------------- /requirements-dev.txt: -------------------------------------------------------------------------------- 1 | ipython 2 | flake8 3 | autopep8 4 | django_debug_toolbar 5 | autoflake 6 | black 7 | isort 8 | pre-commit 9 | -------------------------------------------------------------------------------- /announce/urls.py: -------------------------------------------------------------------------------- 1 | from django.urls import path 2 | 3 | import announce.views 4 | 5 | urlpatterns = [ 6 | path('', announce.views.announce), 7 | ] 8 | -------------------------------------------------------------------------------- /templates/rss/change_archive.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | 3 | {% block hos_content %} 4 | {% include "rss/recentchanges.inc" %} 5 | {% endblock %} 6 | -------------------------------------------------------------------------------- /projects/templates/projects/project_archive.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | 3 | {% block hos_content %} 4 | {% include "projects/overview.inc" %} 5 | {% endblock %} 6 | -------------------------------------------------------------------------------- /templates/500.html: -------------------------------------------------------------------------------- 1 |
3 | Whoops, something happened. We have been notified and will look into it - promise!
4 | -------------------------------------------------------------------------------- /templates/jour_fixe_reminder.mail.subject: -------------------------------------------------------------------------------- 1 | {% load i18n %} 2 | {% language 'de' %} 3 | [REMINDER] Jour Fixe am {{ jf.startDate | date:"l, Y-m-d" }} 4 | {% endlanguage %} 5 | -------------------------------------------------------------------------------- /things/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | 3 | 4 | class ThingsConfig(AppConfig): 5 | default_auto_field = 'django.db.models.BigAutoField' 6 | name = 'things' 7 | -------------------------------------------------------------------------------- /static/stylesheets/base.css: -------------------------------------------------------------------------------- 1 | /* 2 | DJANGO Admin 3 | by Wilson Miner wilson@lawrence.com 4 | */ 5 | 6 | /* Import other styles */ 7 | @import url('reset.css'); 8 | @import url('global.css'); 9 | @import url('layout.css'); 10 | -------------------------------------------------------------------------------- /templates/registration/logged_out.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | 3 | {% block hos_content %} 4 | 5 |You have been logged out.
6 | 7 | 8 | 9 | {% endblock %} 10 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.pyc 2 | *.sqlite 3 | 4 | mos/settings/local* 5 | mos/settings/secret_key.* 6 | web/performance.log 7 | mos_env/ 8 | env/ 9 | 10 | logs/ 11 | media/ 12 | 13 | .vagrant 14 | /docker/volume-* 15 | 16 | matrix_client_store/ 17 | -------------------------------------------------------------------------------- /templates/auth/user_list_mainpage.inc: -------------------------------------------------------------------------------- 1 | 4 | -------------------------------------------------------------------------------- /cal/admin.py: -------------------------------------------------------------------------------- 1 | from django.contrib import admin 2 | 3 | from .models import Category 4 | from .models import Event 5 | from .models import Location 6 | 7 | admin.site.register(Event) 8 | admin.site.register(Category) 9 | admin.site.register(Location) 10 | -------------------------------------------------------------------------------- /templates/auth/user_list.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | {% block hos_content %} 3 | {% if user.is_superuser %} 4 | {% include 'auth/user_list_superuser.inc' %} 5 | {% else %} 6 | {% include "auth/user_list.inc" %} 7 | {% endif %} 8 | {% endblock %} 9 | -------------------------------------------------------------------------------- /members/templates/admin/change_form.html: -------------------------------------------------------------------------------- 1 | {% extends 'admin/change_form.html' %} 2 | {% load static %} 3 | 4 | {% block admin_change_form_document_ready %} 5 | {{ block.super }} 6 | 7 | {% endblock %} 8 | -------------------------------------------------------------------------------- /static/stylesheets/projects.css: -------------------------------------------------------------------------------- 1 | .project .hoverHidden { 2 | visibility: hidden; 3 | } 4 | 5 | .project:hover .hoverHidden { 6 | visibility: visible; 7 | opacity: 0.6; 8 | } 9 | 10 | .project:hover .hoverHidden:hover { 11 | opacity: 1.0; 12 | } 13 | -------------------------------------------------------------------------------- /templates/rss/recentchanges.inc: -------------------------------------------------------------------------------- 1 |Your password was changed.
10 | 11 | {% endblock %} 12 | -------------------------------------------------------------------------------- /templates/cal/event_detail.html: -------------------------------------------------------------------------------- 1 | {% extends 'base.html' %} 2 | {% load makeform %} 3 | 4 | {% block title %}Metalab Event: {{ event.name }}{% endblock %} 5 | {% block hos_content %} 6 | {% makeform event cal.forms.EventForm event_form %} 7 | {% include "cal/eventinfo_detail.inc" with new=0 %} 8 | {% endblock %} 9 | -------------------------------------------------------------------------------- /members/middleware.py: -------------------------------------------------------------------------------- 1 | from django.contrib.auth import logout 2 | from django.utils.deprecation import MiddlewareMixin 3 | 4 | 5 | class DeactivateUserMiddleware(MiddlewareMixin): 6 | def process_request(self, request): 7 | if request.user.is_authenticated and not request.user.is_active: 8 | return logout(request) 9 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | Django~=4.2.10 2 | Pillow 3 | icalendar~=5.0.11 4 | Unipath 5 | django-extensions 6 | python-dateutil 7 | freezegun 8 | feedparser 9 | mysqlclient 10 | channels 11 | websockets 12 | sepaxml 13 | daphne 14 | requests 15 | easy-thumbnails 16 | beautifulsoup4==4.12.3 17 | matrix-commander==7.6.1 18 | django-import-export==4.3.14 19 | -------------------------------------------------------------------------------- /projects/templates/projects/project_detail.html: -------------------------------------------------------------------------------- 1 | {% extends 'base.html' %} 2 | {% load makeform %} 3 | 4 | {% block title %}Metalab Project: {{ project.name }}{% endblock %} 5 | {% block hos_content %} 6 | {% makeform project projects.forms.ProjectForm project_form %} 7 |5 |
7 | Die Nachricht wurde versendet. 8 |
9 | 10 |11 | Betreff: {{ form.cleaned_data.subject }} 12 |
13 | 14 | Body: 15 |16 | {{ form.cleaned_data.body }} 17 |
18 | 19 | Users ({{ users.count }}): 20 || {{ info_dict.member.membership_number }} | 17 |{{ info_dict.user }} | 18 |{{ info_dict.member.begin_of_membership }} | 19 |{{ info_dict.kind }} | 20 |
Your username and password didn't match. Please try again.
7 | {% endif %} 8 | 9 | Please enter your username and password to log in: 10 | 11 | 20 | 21 | 22 | 23 | {% endblock %} 24 | -------------------------------------------------------------------------------- /things/migrations/0002_auto_20240224_1354.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.2.20 on 2024-02-24 13:54 2 | 3 | from django.db import migrations 4 | from django.db import models 5 | 6 | import things.models 7 | 8 | 9 | class Migration(migrations.Migration): 10 | 11 | dependencies = [ 12 | ('things', '0001_initial'), 13 | ] 14 | 15 | operations = [ 16 | migrations.AddField( 17 | model_name='thing', 18 | name='token', 19 | field=models.CharField(default=things.models.make_token, max_length=128), 20 | ), 21 | migrations.AlterField( 22 | model_name='thing', 23 | name='name', 24 | field=models.CharField(max_length=64, unique=True), 25 | ), 26 | ] 27 | -------------------------------------------------------------------------------- /mos/asgi.py: -------------------------------------------------------------------------------- 1 | """ 2 | ASGI entrypoint. Configures Django and then runs the application 3 | defined in the ASGI_APPLICATION setting. 4 | """ 5 | 6 | import os 7 | 8 | import django 9 | from channels.auth import AuthMiddlewareStack 10 | from channels.routing import ProtocolTypeRouter 11 | from channels.routing import URLRouter 12 | from django.core.asgi import get_asgi_application 13 | 14 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "settings.devel") 15 | django.setup() 16 | 17 | django_asgi_app = get_asgi_application() 18 | 19 | application = ProtocolTypeRouter({ 20 | # Django's ASGI application to handle traditional HTTP requests 21 | "http": django_asgi_app, 22 | "websocket": AuthMiddlewareStack( 23 | URLRouter([ 24 | ]) 25 | ), 26 | }) 27 | -------------------------------------------------------------------------------- /core/models.py: -------------------------------------------------------------------------------- 1 | from django.db import models 2 | 3 | 4 | class Category(models.Model): 5 | """ 6 | represents a Category (name, description) 7 | used in cal.models.Event 8 | """ 9 | slug = models.SlugField(null=True) 10 | name = models.CharField(max_length=30) 11 | description = models.CharField(max_length=200) 12 | 13 | def __str__(self): 14 | return self.name 15 | 16 | 17 | class Location(models.Model): 18 | """ 19 | represents a location(name, description, country) 20 | used in cal.models.Event 21 | """ 22 | name = models.CharField(max_length=30) 23 | description = models.CharField(max_length=200) 24 | country = models.CharField(max_length=100) 25 | 26 | def __str__(self): 27 | return self.name 28 | -------------------------------------------------------------------------------- /members/migrations/0021_communicationrecord_monthly_fee_at_contact_and_more.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 4.2.18 on 2025-02-01 22:37 2 | 3 | from django.db import migrations 4 | from django.db import models 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | dependencies = [ 10 | ("members", "0020_communicationrecord"), 11 | ] 12 | 13 | operations = [ 14 | migrations.AddField( 15 | model_name="communicationrecord", 16 | name="monthly_fee_at_contact", 17 | field=models.IntegerField(null=True), 18 | ), 19 | migrations.AddField( 20 | model_name="communicationrecord", 21 | name="outstanding_fees_at_contact", 22 | field=models.IntegerField(null=True), 23 | ), 24 | ] 25 | -------------------------------------------------------------------------------- /members/migrations/0022_contactinfo_in_intern_matrix_room_and_more.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 4.2.10 on 2025-10-08 19:24 2 | 3 | from django.db import migrations 4 | from django.db import models 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | dependencies = [ 10 | ('members', '0021_communicationrecord_monthly_fee_at_contact_and_more'), 11 | ] 12 | 13 | operations = [ 14 | migrations.AddField( 15 | model_name='contactinfo', 16 | name='in_intern_matrix_room', 17 | field=models.BooleanField(default=False), 18 | ), 19 | migrations.AddField( 20 | model_name='contactinfo', 21 | name='matrix_handle', 22 | field=models.CharField(blank=True, max_length=255), 23 | ), 24 | ] 25 | -------------------------------------------------------------------------------- /core/context_processors.py: -------------------------------------------------------------------------------- 1 | from django.conf import settings 2 | 3 | 4 | def custom_settings_global(request): 5 | """ 6 | sets custom style and name as global template variables 7 | """ 8 | return {'custom_style': settings.HOS_CUSTOM_STYLE, 9 | 'HOS_NAME': settings.HOS_NAME, 10 | } 11 | 12 | 13 | def custom_settings_main(request): 14 | """ 15 | sets customizations specified in settings.py for the main page 16 | """ 17 | return {'introduction_text': settings.HOS_INTRODUCTION, 18 | 'members': settings.HOS_MEMBER_GALLERY, 19 | 'openlab': settings.HOS_OPENLAB, 20 | 'calendar': settings.HOS_CALENDAR, 21 | 'projects': settings.HOS_PROJECTS, 22 | 'recent_changes': settings.HOS_RECENT_CHANGES, 23 | } 24 | -------------------------------------------------------------------------------- /announce/templates/announce/write_message.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | 3 | {% block hos_content %} 4 |7 | Diese Vorrichtung versendet Nachrichten an alle derzeit aktiven 8 | Mitglieder des Metalabs. Mißbrauch wird Folgen haben! 9 |
10 |
11 | Die folgenden Variablen können im Body verwendet werden:
12 | {% verbatim %}
13 | {{username}} {{full_name}} {{short_name}} {{first_name}} {{last_name}} {{user_id}} {{profile_link}} {{IBAN}} {{BIC}}
14 | {% endverbatim %}
15 |
{% if item.contactinfo.get_wikilink %}{% endif %}{{ item.username }}{% if item.contactinfo.get_wikilink %}{% endif %}
5 |{% if item.contactinfo.get_wikilink %}{% endif %}{{ item.username }}{% if item.contactinfo.get_wikilink %}{% endif %}
5 ||
6 | {% if item.contactinfo.image %}
7 | |
12 |
13 | {{ item }} 14 | member since: {{ item.contactinfo.get_date_of_first_join|date }} 15 | {# debts: {{ item.contactinfo.get_debts }} Euro #} 16 | |
17 |
18 | edit 19 | {% if item.contactinfo.get_wikilink %} 20 | wikiprofile 21 | {% endif %} 22 | profile 23 | |
24 | |||||||||||||||||||||||||||||||||||||
month |
14 | number of member |
15 | + |
16 | - |
17 | |
{{ l.month.month }}/{{ l.month.year }} |
21 | {{ l.num_member }} |
22 | {{ l.new_member }} |
24 | {{ l.resigned_member }} |
25 | |
no member in database
30 | {% endif %} 31 | {% endblock %} 32 | -------------------------------------------------------------------------------- /things/migrations/0003_auto_20240224_1415.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.2.20 on 2024-02-24 14:15 2 | 3 | import django.db.models.deletion 4 | import django.utils.timezone 5 | from django.conf import settings 6 | from django.db import migrations 7 | from django.db import models 8 | 9 | 10 | class Migration(migrations.Migration): 11 | 12 | dependencies = [ 13 | migrations.swappable_dependency(settings.AUTH_USER_MODEL), 14 | ('things', '0002_auto_20240224_1354'), 15 | ] 16 | 17 | operations = [ 18 | migrations.AddField( 19 | model_name='thinguser', 20 | name='best_before', 21 | field=models.DateField(blank=True, help_text='Wann die Schulung wiederholt werden sollte', null=True), 22 | ), 23 | migrations.AddField( 24 | model_name='thinguser', 25 | name='created_at', 26 | field=models.DateField(auto_now_add=True, default=django.utils.timezone.now), 27 | preserve_default=False, 28 | ), 29 | migrations.AlterField( 30 | model_name='thinguser', 31 | name='user', 32 | field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='thingusers', to=settings.AUTH_USER_MODEL), 33 | ), 34 | ] 35 | -------------------------------------------------------------------------------- /members/migrations/0015_auto_20230709_2031.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.2.20 on 2023-07-09 20:31 2 | 3 | import django.db.models.deletion 4 | from django.conf import settings 5 | from django.db import migrations 6 | from django.db import models 7 | 8 | 9 | class Migration(migrations.Migration): 10 | 11 | dependencies = [ 12 | migrations.swappable_dependency(settings.AUTH_USER_MODEL), 13 | ('members', '0014_auto_20230709_2019'), 14 | ] 15 | 16 | operations = [ 17 | migrations.AlterField( 18 | model_name='bankimportmatcher', 19 | name='color', 20 | field=models.CharField(blank=True, help_text="if action=color, e.g. 'red' or 'rgba(255,0,0,0.1)'", max_length=100, null=True), 21 | ), 22 | migrations.AlterField( 23 | model_name='bankimportmatcher', 24 | name='matcher', 25 | field=models.CharField(help_text='match in IBAN, sender, text', max_length=80), 26 | ), 27 | migrations.AlterField( 28 | model_name='bankimportmatcher', 29 | name='member', 30 | field=models.ForeignKey(blank=True, help_text='if action=match_to', null=True, on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL), 31 | ), 32 | ] 33 | -------------------------------------------------------------------------------- /members/migrations/0008_pendingpayment.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.2.20 on 2023-07-08 15:04 2 | 3 | import django.db.models.deletion 4 | from django.conf import settings 5 | from django.db import migrations 6 | from django.db import models 7 | 8 | 9 | class Migration(migrations.Migration): 10 | 11 | dependencies = [ 12 | migrations.swappable_dependency(settings.AUTH_USER_MODEL), 13 | ('members', '0007_alter_contactinfo_image'), 14 | ] 15 | 16 | operations = [ 17 | migrations.CreateModel( 18 | name='PendingPayment', 19 | fields=[ 20 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 21 | ('amount', models.FloatField()), 22 | ('comment', models.CharField(blank=True, max_length=200)), 23 | ('date', models.DateField()), 24 | ('method', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='members.paymentmethod')), 25 | ('user', models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), 26 | ], 27 | options={ 28 | 'ordering': ['date'], 29 | 'abstract': False, 30 | }, 31 | ), 32 | ] 33 | -------------------------------------------------------------------------------- /things/migrations/0001_initial.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.2.20 on 2024-02-24 13:48 2 | 3 | import django.db.models.deletion 4 | from django.conf import settings 5 | from django.db import migrations 6 | from django.db import models 7 | 8 | 9 | class Migration(migrations.Migration): 10 | 11 | initial = True 12 | 13 | dependencies = [ 14 | migrations.swappable_dependency(settings.AUTH_USER_MODEL), 15 | ] 16 | 17 | operations = [ 18 | migrations.CreateModel( 19 | name='Thing', 20 | fields=[ 21 | ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 22 | ('slug', models.SlugField(unique=True)), 23 | ('name', models.TextField(unique=True)), 24 | ], 25 | ), 26 | migrations.CreateModel( 27 | name='ThingUser', 28 | fields=[ 29 | ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 30 | ('thing', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='things.thing')), 31 | ('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), 32 | ], 33 | ), 34 | ] 35 | -------------------------------------------------------------------------------- /things/migrations/0006_thingevent.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.2.20 on 2024-02-24 16:54 2 | 3 | import django.db.models.deletion 4 | from django.conf import settings 5 | from django.db import migrations 6 | from django.db import models 7 | 8 | 9 | class Migration(migrations.Migration): 10 | 11 | dependencies = [ 12 | migrations.swappable_dependency(settings.AUTH_USER_MODEL), 13 | ('things', '0005_auto_20240224_1447'), 14 | ] 15 | 16 | operations = [ 17 | migrations.CreateModel( 18 | name='ThingEvent', 19 | fields=[ 20 | ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 21 | ('kind', models.CharField(choices=[('LOGIN', 'Login'), ('LOGOUT', 'Logout'), ('USAGE_MEMBER', 'Zeit (Member)'), ('USAGE_NONMEMBER', 'Zeit (Nicht-Member)')], db_index=True, max_length=32)), 22 | ('usage_seconds', models.PositiveIntegerField()), 23 | ('created_at', models.DateTimeField(auto_now_add=True)), 24 | ('thing', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='things.thing')), 25 | ('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='thingevents', to=settings.AUTH_USER_MODEL)), 26 | ], 27 | ), 28 | ] 29 | -------------------------------------------------------------------------------- /members/urls.py: -------------------------------------------------------------------------------- 1 | from django.urls import include 2 | from django.urls import path 3 | from django.urls import re_path 4 | from django.views.generic.list import ListView 5 | 6 | import members.views 7 | 8 | from .models import get_active_members 9 | 10 | username_patterns = [ 11 | path(r'update/userpic/', members.views.members_update_userpic), 12 | re_path(r'^update/(?P| 6 | | name | 7 |member since | 8 |9 | | monthly fee | 10 |debts | 11 | 12 |||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
16 | {% if item.contactinfo.image %}
17 | |
22 |
23 | {{ item }} 24 | |
25 |
26 | {{ item.contactinfo.get_date_of_first_join|date:"Y-m-d" }} 27 | |
28 |
29 | {% if item.contactinfo.get_wikilink %}
30 | wikiprofile 31 | {% endif %} 32 | profile 33 | {{item.first_name}} {{item.last_name}} 34 | |
35 | 36 | {{ item.contactinfo.get_debt_for_this_month|floatformat:2 }} Euro 37 | | 38 |39 | {{ item.contactinfo.get_debts|floatformat:2 }} Euro 40 | | 41 |||||||||
| Month | 15 |Fee kinds | 16 |Spind kinds | 17 |Fees Membership | 18 |Fees Spind | 19 |Fees | 20 |Total Payments | 21 |
| {{ month.month|date:"Y-m" }} | 25 |
26 | {% for kind, count in month.fee_category_kinds.items %}
27 | {{ count }}x {{ kind }} 28 | {% endfor %} 29 | |
30 |
31 | {% for kind, count in month.spind_kinds.items %}
32 | {{ count }}x {{ kind }} 33 | {% endfor %} 34 | |
35 | {{ month.total_fees_membership|floatformat:2 }} Euro | 36 |{{ month.total_fees_spind|floatformat:2 }} Euro | 37 |{{ month.total_fees|floatformat:2 }} Euro | 38 |{{ month.total_payments|floatformat:2 }} Euro | 39 |
Hint call me like this: /member/hetti/?start_date=2022-01-01&end_date=2022-08-01
43 | {% endblock %} 44 | -------------------------------------------------------------------------------- /members/management/commands/member_categories.py: -------------------------------------------------------------------------------- 1 | from collections import defaultdict 2 | from datetime import date 3 | 4 | from django.core.management.base import LabelCommand 5 | from django.db.models import Q 6 | 7 | from ...models import get_active_members_for 8 | 9 | 10 | class Command(LabelCommand): 11 | help = "My shiny new management command." 12 | 13 | def handle_label(self, label, **options): 14 | year = int(label) 15 | 16 | print(f'Mitgliedskategorien je Monat im Jahr {year}:') 17 | for month in range(1, 13): 18 | dt = date(year, month, 1) 19 | data_dict, sum_users = self.handle_date(dt) 20 | print(dt.strftime('%m/%Y'), 'Member in Summe:', sum_users) 21 | for key, value in data_dict.items(): 22 | print(key, value) 23 | 24 | print("\n") 25 | 26 | def handle_date(self, dt): 27 | member_category_dict = defaultdict(int) 28 | user_sum = 0 29 | for user in get_active_members_for(dt): 30 | # we have to get first() because the last day of one period 31 | # may be the same as the first day of the next period. 32 | # this shouldn't happen, but oh well... 33 | # also get_active_members_for uses begin <= dt <= end 34 | period = user.membershipperiod_set.filter( 35 | Q(begin__lte=dt), 36 | Q(end__isnull=True) | Q(end__gte=dt) 37 | ).first() 38 | 39 | member_category_dict[period.kind_of_membership.name] += 1 40 | user_sum += 1 41 | 42 | return member_category_dict, user_sum 43 | -------------------------------------------------------------------------------- /core/middleware.py: -------------------------------------------------------------------------------- 1 | """ 2 | django_playground.core.middleware 3 | inspired by www.pylucid.org 4 | see http://trac.pylucid.net/browser/trunk/pylucid/PyLucid/middlewares/\ 5 | pagestats.py for authors 6 | """ 7 | import time 8 | 9 | from django.db import connection 10 | from django.utils.deprecation import MiddlewareMixin 11 | from django.utils.encoding import force_str 12 | 13 | from core.utils import human_readable_time 14 | 15 | TAG = '' 16 | FOOTER_STAT_STRING = 'renderd in %(render_time)s - %(queries)s sql queries' 17 | 18 | 19 | class SetStatFooter(MiddlewareMixin): 20 | """ 21 | Sets some performance data (number of queries,.. 22 | """ 23 | 24 | def process_request(self, request): 25 | self.time_started = time.time() 26 | self.old_queries = len(connection.queries) 27 | 28 | def process_response(self, request, response): 29 | try: 30 | if 'text/html' not in response['Content-Type']: 31 | return response 32 | if request.headers.get('x-requested-with') == 'XMLHttpRequest': 33 | return response 34 | if response.status_code != 200: 35 | return response 36 | 37 | queries = len(connection.queries) - self.old_queries 38 | 39 | render_time = human_readable_time(time.time() - self.time_started) 40 | stats = FOOTER_STAT_STRING % {'render_time': render_time, 41 | 'queries': queries} 42 | content = response.content 43 | response.content = force_str(content).replace(TAG, stats) 44 | except: 45 | pass 46 | 47 | return response 48 | -------------------------------------------------------------------------------- /core/templatetags/makeform.py: -------------------------------------------------------------------------------- 1 | from django import template 2 | 3 | register = template.Library() 4 | 5 | 6 | @register.tag(name="makeform") 7 | def do_makeform(parser, token): 8 | """ do_makeform variable path.to.form.class target_name """ 9 | try: 10 | tag_name, context_var, form_path, target_name = token.split_contents() 11 | except ValueError: 12 | raise template.TemplateSyntaxError("%r tag requires two arguments" % token.contents.split()[0]) 13 | 14 | try: 15 | modulename, classname = form_path.rsplit('.', 1) 16 | module = __import__(modulename, fromlist=[classname]) 17 | except: 18 | raise template.TemplateSyntaxError("the path to form class (%s) should be import-able! %r" % (form_path.rsplit('.', 1)[0], token.contents.split()[0])) 19 | 20 | cls = getattr(module, classname) 21 | 22 | return MakeFormNode(context_var, cls, target_name) 23 | 24 | 25 | class MakeFormNode(template.Node): 26 | def __init__(self, context_var, cls, target_name): 27 | self.context_var = template.Variable(context_var) if context_var != 'None' else None 28 | self.cls = cls 29 | self.target_name = target_name 30 | 31 | def render(self, context): 32 | if self.context_var: 33 | try: 34 | actual_var = self.context_var.resolve(context) 35 | except template.VariableDoesNotExist: 36 | raise template.TemplateSyntaxError('MakeFormNode cannot resolve variable') 37 | 38 | if not self.context_var: 39 | context[self.target_name] = self.cls() 40 | else: 41 | context[self.target_name] = self.cls(instance=actual_var) 42 | 43 | return '' 44 | -------------------------------------------------------------------------------- /members/migrations/0020_communicationrecord.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 4.2.18 on 2025-02-01 22:31 2 | 3 | import django.db.models.deletion 4 | from django.conf import settings 5 | from django.db import migrations 6 | from django.db import models 7 | 8 | 9 | class Migration(migrations.Migration): 10 | 11 | dependencies = [ 12 | migrations.swappable_dependency(settings.AUTH_USER_MODEL), 13 | ("members", "0019_pendingpayment_creator"), 14 | ] 15 | 16 | operations = [ 17 | migrations.CreateModel( 18 | name="CommunicationRecord", 19 | fields=[ 20 | ( 21 | "id", 22 | models.AutoField( 23 | auto_created=True, 24 | primary_key=True, 25 | serialize=False, 26 | verbose_name="ID", 27 | ), 28 | ), 29 | ("contacted_on", models.DateField()), 30 | ("initial_contact", models.BooleanField(default=False)), 31 | ( 32 | "contacted_by", 33 | models.CharField(blank=True, max_length=100, null=True), 34 | ), 35 | ("contact_resolved", models.BooleanField(default=False)), 36 | ("comment", models.CharField(blank=True, max_length=1000, null=True)), 37 | ( 38 | "user", 39 | models.ForeignKey( 40 | on_delete=django.db.models.deletion.CASCADE, 41 | to=settings.AUTH_USER_MODEL, 42 | ), 43 | ), 44 | ], 45 | ), 46 | ] 47 | -------------------------------------------------------------------------------- /projects/views.py: -------------------------------------------------------------------------------- 1 | from django.contrib.auth.decorators import login_required 2 | from django.shortcuts import get_object_or_404 3 | from django.shortcuts import render 4 | from django.views.decorators.http import require_http_methods 5 | 6 | from .forms import ProjectForm 7 | from .models import Project 8 | 9 | 10 | @login_required 11 | def update_project(request, object_id=None): 12 | """ 13 | Updates or creates a project and returns a view with a project form. 14 | """ 15 | project = None if object_id is None else Project.all.get(id=object_id) 16 | 17 | if request.method == 'POST': 18 | project_form = ProjectForm(request.POST, instance=project) 19 | if project_form.is_valid(): 20 | project = project_form.save(commit=False) 21 | if project.created_by_id is None: 22 | project.created_by = request.user 23 | project.save() 24 | else: 25 | project_form = ProjectForm() 26 | 27 | return render(request, 'projects/projectinfo.inc', { 28 | 'project_form': project_form, 29 | 'project': project, 30 | }) 31 | 32 | 33 | @login_required 34 | @require_http_methods(['POST']) 35 | def delete_project(request, object_id=None): 36 | """Delete a project""" 37 | project = get_object_or_404(Project, id=object_id) 38 | project.delete() 39 | return _get_latest(request) 40 | 41 | 42 | def _get_latest(request, current_project=None, errors=None, 43 | e_project_name=None): 44 | """ Returns a view that displays the latest 5 projects """ 45 | 46 | latest = Project.all.order_by('-created_at')[:5] 47 | return render(request, 'projects/overview.inc', { 48 | 'project': current_project, 49 | 'latestprojects': latest, 50 | 'errors': errors, 51 | 'e_project_name': e_project_name, 52 | }) 53 | -------------------------------------------------------------------------------- /cal/migrations/0001_initial.py: -------------------------------------------------------------------------------- 1 | import datetime 2 | 3 | from django.conf import settings 4 | from django.db import migrations 5 | from django.db import models 6 | 7 | 8 | class Migration(migrations.Migration): 9 | 10 | dependencies = [ 11 | migrations.swappable_dependency(settings.AUTH_USER_MODEL), 12 | ('core', '__first__'), 13 | ] 14 | 15 | operations = [ 16 | migrations.CreateModel( 17 | name='Event', 18 | fields=[ 19 | ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), 20 | ('name', models.CharField(max_length=200)), 21 | ('teaser', models.TextField(max_length=200, null=True, blank=True)), 22 | ('wikiPage', models.CharField(max_length=200)), 23 | ('startDate', models.DateTimeField()), 24 | ('endDate', models.DateTimeField(null=True, blank=True)), 25 | ('who', models.CharField(max_length=200, blank=True)), 26 | ('where', models.CharField(max_length=200, blank=True)), 27 | ('created_at', models.DateTimeField(default=datetime.datetime.now)), 28 | ('deleted', models.BooleanField(default=False)), 29 | ('category', models.ForeignKey( 30 | blank=True, 31 | on_delete=models.CASCADE, 32 | to='core.Category', 33 | null=True, 34 | )), 35 | ('created_by', models.ForeignKey( 36 | to=settings.AUTH_USER_MODEL, 37 | on_delete=models.CASCADE, 38 | )), 39 | ('location', models.ForeignKey( 40 | blank=True, 41 | to='core.Location', 42 | on_delete=models.CASCADE, 43 | null=True, 44 | )), 45 | ], 46 | ), 47 | ] 48 | -------------------------------------------------------------------------------- /templates/cal/eventinfo_nf.inc: -------------------------------------------------------------------------------- 1 | 2 |7 | {{ event.startDate|date:"D d.m.Y H:i" }} {% if event.endDate %} - {% if event.start_end_date_eq %} {{ event.endDate|date:"H:i" }} {% else %} {{ event.endDate|date:"D d.m.Y H:i" }} {% endif %} {% endif %} 8 | {% if event.wikiPage %} {% endif %}{{ event.name }}{% if event.wikiPage %}{% endif %} 9 | {% if event.teaser %}{% endif %} 10 | {% if event.category %} | {{ event.category }}{% endif %} 11 | {% if event.location%} | {{ event.location }}{% endif %} 12 | 13 | {% if user.is_authenticated and not edit_disabled %} 14 | 15 | Edit 16 | 17 | {%endif %} 18 | 📅 ical 19 | {% if user.is_authenticated %} 20 | | created by {{ event.created_by }} 21 | {%endif %} 22 |
23 | 24 | {%else%} 25 |Download current events in ical format
26 | {% if user.is_authenticated and not edit_disabled %} 27 |28 | 29 | Create new event 30 | 31 |
32 | {%endif %} 33 | {%endif %} 34 | 35 | 36 | {% if user.is_authenticated %} 37 | {% include "cal/event_form.inc" with from_nf=1 %} 38 | {% endif %} 39 |6 | HOWTO 7 | 8 | * unten sind alle eingelesenen Buchungen, Buchungen von Wien Energie und co werden ignoriert 9 | * Member werden anhand ihrer eingetragenen IBAN vorausgewählt 10 | * Rückläufer sind rot 11 | * schon gebuchte Zeilen sind grau und nicht vorausgewählt 12 | 13 |14 | 65 | {% endblock %} 66 | -------------------------------------------------------------------------------- /projects/templates/projects/projectinfo.inc: -------------------------------------------------------------------------------- 1 | {% with form_id=project.id|default_if_none:'' %} 2 |
4 | {% if project.wikiPage %} {% endif %}{{ project.name }}{% if project.wikiPage %}{% endif %}
5 | {% if user.is_authenticated %}{% if project %}Edit{% else %}Create New Project{% endif %}{% endif %}{% if project.teaser %}
{{ project.teaser }}{% endif %}
6 |