Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Store user visit data #15

Merged
merged 3 commits into from
Apr 10, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 25 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
**/__pycache__
**/.classpath
**/.dockerignore
**/.env
**/.git
**/.gitignore
**/.project
**/.settings
**/.toolstarget
**/.vs
**/.vscode
**/*.*proj.user
**/*.dbmdl
**/*.jfm
**/bin
**/charts
**/docker-compose*
**/compose*
**/Dockerfile*
**/node_modules
**/npm-debug.log
**/obj
**/secrets.dev.yaml
**/values.dev.yaml
README.md
38 changes: 38 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
# For more information, please refer to https://aka.ms/vscode-docker-python
FROM python:3.8-slim

EXPOSE 8000

# Keeps Python from generating .pyc files in the container
ENV PYTHONDONTWRITEBYTECODE=1

# Turns off buffering for easier container logging
ENV PYTHONUNBUFFERED=1

RUN apt-get update
RUN echo "Installing GDAL dependencies" && \
apt-get install -y libgdal-dev libcurl4-gnutls-dev librtmp-dev && \
echo "Installing other depdencies" && \
apt-get install -y wait-for-it curl sudo && \
echo "Install C library for geoip2" && \
apt install libmaxminddb0 libmaxminddb-dev mmdb-bin && \
echo "Removing build dependencies and cleaning up" && \
rm -rf /var/lib/apt/lists/* && \
rm -rf ~/.cache/pip

# Install pip requirements
ADD REQUIREMENTS.txt .
RUN python -m pip install -r REQUIREMENTS.txt

RUN apt-get update && apt-get install -y curl && curl -LJO https://github.com/P3TERX/GeoLite.mmdb/raw/download/GeoLite2-City.mmdb && \
mkdir /var/opt/maxmind && \
mv GeoLite2-City.mmdb /var/opt/maxmind/GeoLite2-City.mmdb

ENV GEOIP_PATH=/var/opt/maxmind/

WORKDIR /code
ADD . /code

# Creates a non-root user
# During debugging, this entry point will be overridden. For more information, please refer to https://aka.ms/vscode-docker-python-debug
CMD ["gunicorn", "--bind", "0.0.0.0:8000", "qgisfeedproject.wsgi"]
23 changes: 19 additions & 4 deletions Dockerfile.testing
Original file line number Diff line number Diff line change
@@ -1,10 +1,25 @@
FROM python:3
FROM python:3.7
ENV PYTHONUNBUFFERED 1
RUN apt-get update && apt install -y libgdal20 wait-for-it
RUN apt-get update
RUN echo "Installing GDAL dependencies" && \
apt-get install -y libgdal-dev libcurl4-gnutls-dev librtmp-dev && \
echo "Installing other depdencies" && \
apt-get install -y wait-for-it && \
echo "Install C library for geoip2" && \
apt install libmaxminddb0 libmaxminddb-dev mmdb-bin && \
echo "Removing build dependencies and cleaning up" && \
rm -rf /var/lib/apt/lists/* && \
rm -rf ~/.cache/pip

RUN mkdir /code
WORKDIR /code

RUN apt-get update && apt-get install -y curl && curl -LJO https://github.com/P3TERX/GeoLite.mmdb/raw/download/GeoLite2-City.mmdb && \
mkdir /var/opt/maxmind && \
mv GeoLite2-City.mmdb /var/opt/maxmind/GeoLite2-City.mmdb

ENV GEOIP_PATH=/var/opt/maxmind/

COPY REQUIREMENTS.txt /code/
RUN pip install -r REQUIREMENTS.txt
COPY . /code/
COPY ./settings_docker_testing.py /code/qgisfeedproject/qgisfeedproject/settings_local.py
COPY ./entrypoint_testing.sh /code/
3 changes: 3 additions & 0 deletions REQUIREMENTS.txt
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,6 @@ django-imagekit

# DEBUG=True only
django-extensions

django-user-visit==0.5.1
geoip2==4.5.0
26 changes: 26 additions & 0 deletions docker-compose.dev.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
version: '3.7'
services:
postgis:
image: kartoza/postgis:14-3.1
platform: linux/amd64
environment:
POSTGRES_USER: docker
POSTGRES_PASS: docker
POSTGRES_DBNAME: qgisfeed

qgisfeed:
build:
context: .
dockerfile: ./Dockerfile
platform: linux/amd64
command: /code/entrypoint_testing.sh
environment:
DJANGO_SETTINGS_MODULE: qgisfeedproject.settings_dev
ports:
- "8000:8000"
links:
- postgis
depends_on:
- postgis
volumes:
- ../qgis-feed:/code
Empty file added qgisfeedproject/__init__.py
Empty file.
66 changes: 65 additions & 1 deletion qgisfeedproject/qgisfeed/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,10 @@
from django.urls import reverse
from django.utils import timezone

from .models import QgisFeedEntry
from user_visit.admin import UserVisitAdmin
from user_visit.models import UserVisit

from .models import QgisFeedEntry, QgisUserVisit, DailyQgisUserVisit

# Get an instance of a logger
logger = logging.getLogger('qgisfeed.admin')
Expand Down Expand Up @@ -84,4 +87,65 @@ def get_form(self, request, obj=None, **kwargs):
return form


class QgisUserVisitAdmin(admin.StackedInline):
readonly_fields = ('qgis_version', 'location', 'platform')
can_delete = False
model = QgisUserVisit


class DailyQgisUserVisitAdmin(admin.ModelAdmin):
list_display = (
'date',
)

def has_add_permission(self, request):
return False


class UpdatedUserVisitAdmin(UserVisitAdmin):
inlines = [
QgisUserVisitAdmin
]
list_display = ("timestamp", "qgis_version", "country", "platform")
search_fields = (
"qgisuservisit__qgis_version",
"qgisuservisit__location"
)
readonly_fields = (
"timestamp",
"hash",
"session_key",
"user_agent",
"ua_string",
"created_at",
)
exclude = ['user', 'remote_addr']

def qgis_version(self, obj):
qgis_version = ''
if obj.qgisuservisit:
qgis_version = obj.qgisuservisit.qgis_version
if not qgis_version:
qgis_version = '-'
return qgis_version

def country(self, obj):
country = '-'
if obj.qgisuservisit:
if obj.qgisuservisit.location and 'country_name' in obj.qgisuservisit.location:
country = obj.qgisuservisit.location['country_name']
return country

def platform(sel, obj):
platfrom_string = 'Unknown'
if obj.qgisuservisit:
platfrom_string = obj.qgisuservisit.platform
return platfrom_string

qgis_version.short_description = 'QGIS Version'


admin.site.register(QgisFeedEntry, QgisFeedEntryAdmin)
admin.site.unregister(UserVisit)
admin.site.register(UserVisit, UpdatedUserVisitAdmin)
admin.site.register(DailyQgisUserVisit, DailyQgisUserVisitAdmin)
4 changes: 3 additions & 1 deletion qgisfeedproject/qgisfeed/apps.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,9 @@ class QgisFeedConfig(AppConfig):
name = 'qgisfeed'

def ready(self):
from .signals import setup_group
from .signals import setup_group, post_save_user_visit
from django.contrib.auth.models import User
from user_visit.models import UserVisit
post_save.connect(setup_group, sender=User)
post_save.connect(post_save_user_visit, sender=UserVisit)

Empty file.
Empty file.
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# coding=utf-8
from django.core.management.base import BaseCommand
from qgisfeed.models import aggregate_user_visit_data


class Command(BaseCommand):
"""Add site codes
"""

def handle(self, *args, **options):
aggregate_user_visit_data()
25 changes: 25 additions & 0 deletions qgisfeedproject/qgisfeed/middleware.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import typing

from django.http import HttpRequest, HttpResponse
from django.utils import timezone
from django.contrib.auth.models import User

from user_visit.models import UserVisit, parse_remote_addr, parse_ua_string
from user_visit.middleware import UserVisitMiddleware, save_user_visit


class QgisFeedUserVisitMiddleware(UserVisitMiddleware):
"""Middleware to record user visits."""

def __call__(self, request: HttpRequest) -> typing.Optional[HttpResponse]:
if request.user.is_anonymous:
user, _ = User.objects.get_or_create(username='qgis_user')
request.user = user
if not request.session or not request.session.session_key:
request.session.save()

uv = UserVisit.objects.build(request, timezone.now())
if not UserVisit.objects.filter(hash=uv.hash).exists():
save_user_visit(uv)

return self.get_response(request)
30 changes: 30 additions & 0 deletions qgisfeedproject/qgisfeed/migrations/0006_auto_20220325_0652.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# Generated by Django 3.2.12 on 2022-03-25 06:52

from django.db import migrations, models
import imagekit.models.fields
import qgisfeed.models


class Migration(migrations.Migration):

dependencies = [
('qgisfeed', '0005_auto_20190709_0814'),
]

operations = [
migrations.AlterField(
model_name='qgisfeedentry',
name='content',
field=models.TextField(),
),
migrations.AlterField(
model_name='qgisfeedentry',
name='image',
field=imagekit.models.fields.ProcessedImageField(blank=True, height_field='image_height', help_text='Landscape orientation, image will be cropped and scaled automatically to 500x354 px', null=True, upload_to='feedimages/%Y/%m/%d/', verbose_name='Image', width_field='image_width'),
),
migrations.AlterField(
model_name='qgisfeedentry',
name='language_filter',
field=qgisfeed.models.QgisLanguageField(blank=True, choices=[('aa', 'Afar'), ('ab', 'Abkhazian'), ('ae', 'Avestan'), ('af', 'Afrikaans'), ('ak', 'Akan'), ('am', 'Amharic'), ('an', 'Aragonese'), ('ar', 'Arabic'), ('as', 'Assamese'), ('av', 'Avaric'), ('ay', 'Aymara'), ('az', 'Azerbaijani'), ('ba', 'Bashkir'), ('be', 'Belarusian'), ('bg', 'Bulgarian'), ('bh', 'Bihari languages'), ('bi', 'Bislama'), ('bm', 'Bambara'), ('bn', 'Bengali'), ('bo', 'Tibetan'), ('br', 'Breton'), ('bs', 'Bosnian'), ('ca', 'Catalan; Valencian'), ('ce', 'Chechen'), ('ch', 'Chamorro'), ('co', 'Corsican'), ('cr', 'Cree'), ('cs', 'Czech'), ('cu', 'Church Slavic'), ('cv', 'Chuvash'), ('cy', 'Welsh'), ('da', 'Danish'), ('de', 'German'), ('dv', 'Divehi; Dhivehi; Maldivian'), ('dz', 'Dzongkha'), ('ee', 'Ewe'), ('el', 'Greek, Modern'), ('en', 'English'), ('eo', 'Esperanto'), ('es', 'Spanish; Castilian'), ('et', 'Estonian'), ('eu', 'Basque'), ('fa', 'Persian'), ('ff', 'Fulah'), ('fi', 'Finnish'), ('fj', 'Fijian'), ('fo', 'Faroese'), ('fr', 'French'), ('fy', 'Western Frisian'), ('ga', 'Irish'), ('gd', 'Gaelic; Scottish Gaelic'), ('gl', 'Galician'), ('gn', 'Guarani'), ('gu', 'Gujarati'), ('gv', 'Manx'), ('ha', 'Hausa'), ('he', 'Hebrew'), ('hi', 'Hindi'), ('ho', 'Hiri Motu'), ('hr', 'Croatian'), ('ht', 'Haitian; Haitian Creole'), ('hu', 'Hungarian'), ('hy', 'Armenian'), ('hz', 'Herero'), ('ia', 'Interlingua'), ('id', 'Indonesian'), ('ie', 'Interlingue; Occidental'), ('ig', 'Igbo'), ('ii', 'Sichuan Yi; Nuosu'), ('ik', 'Inupiaq'), ('io', 'Ido'), ('is', 'Icelandic'), ('it', 'Italian'), ('iu', 'Inuktitut'), ('ja', 'Japanese'), ('jv', 'Javanese'), ('ka', 'Georgian'), ('kg', 'Kongo'), ('ki', 'Kikuyu; Gikuyu'), ('kj', 'Kuanyama; Kwanyama'), ('kk', 'Kazakh'), ('kl', 'Kalaallisut; Greenlandic'), ('km', 'Central Khmer'), ('kn', 'Kannada'), ('ko', 'Korean'), ('kr', 'Kanuri'), ('ks', 'Kashmiri'), ('ku', 'Kurdish'), ('kv', 'Komi'), ('kw', 'Cornish'), ('ky', 'Kirghiz; Kyrgyz'), ('la', 'Latin'), ('lb', 'Luxembourgish; Letzeburgesch'), ('lg', 'Ganda'), ('li', 'Limburgan; Limburger; Limburgish'), ('ln', 'Lingala'), ('lo', 'Lao'), ('lt', 'Lithuanian'), ('lu', 'Luba-Katanga'), ('lv', 'Latvian'), ('mh', 'Marshallese'), ('ml', 'Malayalam'), ('mr', 'Marathi'), ('mk', 'Macedonian'), ('mg', 'Malagasy'), ('mt', 'Maltese'), ('mn', 'Mongolian'), ('mi', 'Maori'), ('ms', 'Malay (macrolanguage)'), ('my', 'Burmese'), ('na', 'Nauru'), ('nv', 'Navajo'), ('nr', 'South Ndebele'), ('nd', 'North Ndebele'), ('ng', 'Ndonga'), ('ne', 'Nepali (macrolanguage)'), ('nl', 'Dutch'), ('nn', 'Norwegian Nynorsk'), ('nb', 'Norwegian Bokmål'), ('no', 'Norwegian'), ('ny', 'Nyanja'), ('oc', 'Occitan (post 1500)'), ('oj', 'Ojibwa'), ('or', 'Oriya (macrolanguage)'), ('om', 'Oromo'), ('os', 'Ossetian'), ('pa', 'Panjabi'), ('pi', 'Pali'), ('pl', 'Polish'), ('pt', 'Portuguese'), ('ps', 'Pushto'), ('qu', 'Quechua'), ('rm', 'Romansh'), ('ro', 'Romanian'), ('rn', 'Rundi'), ('ru', 'Russian'), ('sg', 'Sango'), ('sa', 'Sanskrit'), ('si', 'Sinhala'), ('sk', 'Slovak'), ('sl', 'Slovenian'), ('se', 'Northern Sami'), ('sm', 'Samoan'), ('sn', 'Shona'), ('sd', 'Sindhi'), ('so', 'Somali'), ('st', 'Southern Sotho'), ('es', 'Spanish'), ('sq', 'Albanian'), ('sc', 'Sardinian'), ('sr', 'Serbian'), ('ss', 'Swati'), ('su', 'Sundanese'), ('sw', 'Swahili (macrolanguage)'), ('sv', 'Swedish'), ('ty', 'Tahitian'), ('ta', 'Tamil'), ('tt', 'Tatar'), ('te', 'Telugu'), ('tg', 'Tajik'), ('tl', 'Tagalog'), ('th', 'Thai'), ('ti', 'Tigrinya'), ('to', 'Tonga (Tonga Islands)'), ('tn', 'Tswana'), ('ts', 'Tsonga'), ('tk', 'Turkmen'), ('tr', 'Turkish'), ('tw', 'Twi'), ('ug', 'Uighur'), ('uk', 'Ukrainian'), ('ur', 'Urdu'), ('uz', 'Uzbek'), ('ve', 'Venda'), ('vi', 'Vietnamese'), ('vo', 'Volapük'), ('wa', 'Walloon'), ('wo', 'Wolof'), ('xh', 'Xhosa'), ('yi', 'Yiddish'), ('yo', 'Yoruba'), ('za', 'Zhuang'), ('zh', 'Chinese'), ('zu', 'Zulu')], db_index=True, help_text='The entry will be hidden to users who have not set a matching language filter', max_length=3, null=True, verbose_name='Language filter'),
),
]
24 changes: 24 additions & 0 deletions qgisfeedproject/qgisfeed/migrations/0007_qgisuservisit.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# Generated by Django 4.0.3 on 2022-03-26 06:19

from django.db import migrations, models
import django.db.models.deletion


class Migration(migrations.Migration):

dependencies = [
('user_visit', '0002_add_created_at'),
('qgisfeed', '0006_auto_20220325_0652'),
]

operations = [
migrations.CreateModel(
name='QgisUserVisit',
fields=[
('user_visit', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, primary_key=True, serialize=False, to='user_visit.uservisit')),
('location', models.JSONField()),
('qgis_version', models.CharField(blank=True, default='', max_length=255)),
('platform', models.CharField(blank=True, default='', max_length=255)),
],
),
]
23 changes: 23 additions & 0 deletions qgisfeedproject/qgisfeed/migrations/0008_dailyqgisuservisit.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# Generated by Django 4.0.3 on 2022-04-08 09:34

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
('qgisfeed', '0007_qgisuservisit'),
]

operations = [
migrations.CreateModel(
name='DailyQgisUserVisit',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('date', models.DateField(auto_now_add=True)),
('qgis_version', models.JSONField()),
('platform', models.JSONField()),
('country', models.JSONField()),
],
),
]
Loading