RSS-Feed erstellen
--
diff --git a/backend/.gitignore b/backend/.gitignore index d879270..cbeb2f6 100644 --- a/backend/.gitignore +++ b/backend/.gitignore @@ -1,6 +1,7 @@ venv *.pyc staticfiles +media .env *.sqlite3 *.sqlite diff --git a/backend/app_be/models.py b/backend/app_be/models.py index 0695a44..ca59a9a 100644 --- a/backend/app_be/models.py +++ b/backend/app_be/models.py @@ -1,26 +1,25 @@ +from django.core.validators import URLValidator, FileExtensionValidator from django.db import models -# Create your models here. class User(models.Model): pass -class Icon(models.Model): - image = models.ImageField(upload_to='feed_icons') - - class Feed(models.Model): - url = models.CharField(max_length=100) + url = models.TextField(blank=False, null=False, validators=[URLValidator(['http', 'https'])]) active = models.BooleanField() + icon = models.FileField(upload_to='feed-icons', blank=True, null=True, + validators=[FileExtensionValidator(['png', 'svg'])]) + keywords = models.TextField(blank=False, null=False) class FeedEntry(models.Model): + feed = models.ForeignKey(Feed, on_delete=models.CASCADE) tweeted = models.BooleanField() class Tweet(models.Model): - icon = models.ForeignKey(Icon, on_delete=models.CASCADE) text = models.CharField(max_length=137) date_time = models.DateTimeField() url = models.CharField(max_length=100) diff --git a/backend/app_be/serializers.py b/backend/app_be/serializers.py index de8ae38..2b06766 100644 --- a/backend/app_be/serializers.py +++ b/backend/app_be/serializers.py @@ -1,3 +1,26 @@ -from rest_framework import serializers +from rest_framework.exceptions import ValidationError +from rest_framework.serializers import ModelSerializer -# add serializer here +from app_be.models import Feed + + +class FeedSerializer(ModelSerializer): + class Meta: + model = Feed + fields = '__all__' + + def validate_icon(self, value): + if value is not None and value.size > 10240: + raise ValidationError("Invalid icon: Maximum size is 10KB") + return value + + def validate_keywords(self, value): + split = [x.strip() for x in value.split(',')] + if len(split) > 3: + raise ValidationError("Invalid keywords: No more than three entries") + elif len(split) == 0: + raise ValidationError("Invalid keywords: Need at least one entry") + for entry in split: + if len(entry) < 3: + raise ValidationError("Invalid keywords: Keywords have to be of length greater than 2") + return value diff --git a/backend/app_be/settings.py b/backend/app_be/settings.py index c4e2414..883fa0a 100644 --- a/backend/app_be/settings.py +++ b/backend/app_be/settings.py @@ -161,6 +161,9 @@ STATICFILES_DIRS = [ os.path.join(PROJECT_ROOT, 'static'), ] +MEDIA_ROOT = os.path.join(PROJECT_ROOT, 'media') +MEDIA_URL = '/media/' + # Simplified static file serving. # https://warehouse.python.org/project/whitenoise/ STATICFILES_STORAGE = 'whitenoise.storage.CompressedManifestStaticFilesStorage' diff --git a/backend/app_be/urls.py b/backend/app_be/urls.py index ab946a3..0bf03e2 100644 --- a/backend/app_be/urls.py +++ b/backend/app_be/urls.py @@ -28,5 +28,6 @@ urlpatterns = [ ] router = DefaultRouter() +router.register(r'feeds', FeedViewSet, basename='feeds') urlpatterns.extend(router.urls) diff --git a/backend/app_be/views/rest_api.py b/backend/app_be/views/rest_api.py index 17a0c4b..656e104 100644 --- a/backend/app_be/views/rest_api.py +++ b/backend/app_be/views/rest_api.py @@ -2,9 +2,14 @@ import logging from django.http import JsonResponse -from rest_framework.decorators import api_view from py_jwt_validator import PyJwtValidator, PyJwtException +from rest_framework.decorators import api_view +from rest_framework.viewsets import ModelViewSet + +from app_be.models import Feed +from app_be.serializers import FeedSerializer + logger = logging.getLogger(__name__) @@ -58,3 +63,8 @@ class TwitterClass: def getLastSixTweets(): return JsonResponse({[{"asdf", "asdf", "sdfasdf", "asdf"}, {"asdf", "asdf", "sdfasdf", "asdf"}]}, safe=False, status=200) + + +class FeedViewSet(ModelViewSet): + queryset = Feed.objects.all() + serializer_class = FeedSerializer diff --git a/backend/app_be/views/twitter.py b/backend/app_be/views/twitter.py index cfcea34..ba1e1df 100644 --- a/backend/app_be/views/twitter.py +++ b/backend/app_be/views/twitter.py @@ -39,10 +39,10 @@ class twitter_bot: def scan_active_feed(self): starttime = time.time() - while True: + # while True: # Search RSS Feed - time.sleep(60.0 - ((time.time() - starttime) % 60.0)) + # time.sleep(60.0 - ((time.time() - starttime) % 60.0)) twitter_bot = twitter_bot() diff --git a/docker-compose.yml b/docker-compose.yml index b916195..7231e35 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -7,9 +7,9 @@ services: container_name: waecm_g4_be_container hostname: waecm_g4_be image: pfingstfrosch/waecm-2021-group-04-bsp-1-be - # build: - # context: ./backend - # dockerfile: ./Dockerfile + build: + context: ./backend + dockerfile: ./Dockerfile command: python manage.py runserver 0.0.0.0:8000 ports: - 8000:8000 @@ -18,8 +18,8 @@ services: container_name: waecm_g4_fe_container hostname: waecm_g4_fe image: pfingstfrosch/waecm-2021-group-04-bsp-1-fe - # build: - # context: ./frontend - # dockerfile: ./Dockerfile + build: + context: ./frontend + dockerfile: ./Dockerfile ports: - 4200:80 diff --git a/frontend/package-lock.json b/frontend/package-lock.json index d8b14f7..c99d544 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -57,6 +57,7 @@ "karma-coverage-istanbul-reporter": "~2.1.1", "karma-jasmine": "~2.0.1", "karma-jasmine-html-reporter": "^1.5.4", + "ngx-material-file-input": "^2.1.1", "protractor": "~5.4.4", "ts-node": "~8.5.4", "tslint": "~5.20.1", @@ -431,9 +432,6 @@ "version": "9.2.4", "resolved": "https://registry.npmjs.org/@angular/cdk/-/cdk-9.2.4.tgz", "integrity": "sha512-iw2+qHMXHYVC6K/fttHeNHIieSKiTEodVutZoOEcBu9rmRTGbLB26V/CRsfIRmA1RBk+uFYWc6UQZnMC3RdnJQ==", - "dependencies": { - "parse5": "^5.0.0" - }, "optionalDependencies": { "parse5": "^5.0.0" } @@ -969,7 +967,6 @@ "dependencies": { "anymatch": "~3.1.1", "braces": "~3.0.2", - "fsevents": "~2.1.2", "glob-parent": "~5.1.0", "is-binary-path": "~2.1.0", "is-glob": "~4.0.1", @@ -2149,7 +2146,6 @@ "dependencies": { "anymatch": "~3.1.1", "braces": "~3.0.2", - "fsevents": "~2.1.2", "glob-parent": "~5.1.0", "is-binary-path": "~2.1.0", "is-glob": "~4.0.1", @@ -3702,8 +3698,7 @@ "dependencies": { "esprima": "~1.0.4", "estraverse": "~1.5.0", - "esutils": "~1.0.0", - "source-map": "~0.1.30" + "esutils": "~1.0.0" }, "optionalDependencies": { "source-map": "~0.1.30" @@ -6542,8 +6537,7 @@ "esprima": "^4.0.1", "estraverse": "^4.2.0", "esutils": "^2.0.2", - "optionator": "^0.8.1", - "source-map": "~0.6.1" + "optionator": "^0.8.1" }, "optionalDependencies": { "source-map": "~0.6.1" @@ -7559,7 +7553,6 @@ "minimist": "^1.2.5", "neo-async": "^2.6.0", "source-map": "^0.6.1", - "uglify-js": "^3.1.4", "wordwrap": "^1.0.0" }, "optionalDependencies": { @@ -9279,7 +9272,6 @@ "dependencies": { "anymatch": "~3.1.1", "braces": "~3.0.2", - "fsevents": "~2.1.2", "glob-parent": "~5.1.0", "is-binary-path": "~2.1.0", "is-glob": "~4.0.1", @@ -9419,12 +9411,8 @@ "clone": "^2.1.2", "errno": "^0.1.1", "graceful-fs": "^4.1.2", - "image-size": "~0.5.0", - "make-dir": "^2.1.0", "mime": "^1.4.1", - "promise": "^7.1.1", "request": "^2.83.0", - "source-map": "~0.6.0", "tslib": "^1.10.0" }, "optionalDependencies": { @@ -9644,7 +9632,6 @@ "anymatch": "^2.0.0", "async-each": "^1.0.1", "braces": "^2.3.2", - "fsevents": "^1.2.7", "glob-parent": "^3.1.0", "inherits": "^2.0.3", "is-binary-path": "^1.0.0", @@ -10727,6 +10714,19 @@ "vlq": "^1.0.0" } }, + "node_modules/ngx-material-file-input": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ngx-material-file-input/-/ngx-material-file-input-2.1.1.tgz", + "integrity": "sha512-FbaIjiJnL6BZtZYWLvMSn9aSaM62AZaJegloTUphmLz5jopXPzE5W+3aC+dsf9h1IIqHSCLcyv0w+qH0ypBhMA==", + "dev": true, + "peerDependencies": { + "@angular/cdk": "^8.1.1 || ^9.0.0", + "@angular/common": "^8.1.3 || ^9.0.0", + "@angular/core": "^8.1.3 || ^9.0.0", + "@angular/material": "^8.1.1 || ^9.0.0", + "tslib": "^1.10.0" + } + }, "node_modules/nice-try": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", @@ -13626,9 +13626,6 @@ "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.1.0.tgz", "integrity": "sha512-gfE1455AEazVVTJoeQtcOq/U6GSxwoj4XPSWVsuWmgIxj7sBQNLDOSA82PbdMe+cP8ql8fR1jogPFe8Wg8g4SQ==", "dev": true, - "dependencies": { - "fsevents": "~2.1.2" - }, "optionalDependencies": { "fsevents": "~2.1.2" } @@ -13775,7 +13772,6 @@ "dependencies": { "anymatch": "~3.1.1", "braces": "~3.0.2", - "fsevents": "~2.1.2", "glob-parent": "~5.1.0", "is-binary-path": "~2.1.0", "is-glob": "~4.0.1", @@ -16518,10 +16514,8 @@ "integrity": "sha512-aWAgTW4MoSJzZPAicljkO1hsi1oKj/RRq/OJQh2PKI2UKL04c2Bs+MBOB+BBABHTXJpf9mCwHN7ANCvYsvY2sg==", "dev": true, "dependencies": { - "chokidar": "^3.4.1", "graceful-fs": "^4.1.2", - "neo-async": "^2.5.0", - "watchpack-chokidar2": "^2.0.0" + "neo-async": "^2.5.0" }, "optionalDependencies": { "chokidar": "^3.4.1", @@ -17227,7 +17221,6 @@ "anymatch": "^2.0.0", "async-each": "^1.0.1", "braces": "^2.3.2", - "fsevents": "^1.2.7", "glob-parent": "^3.1.0", "inherits": "^2.0.3", "is-binary-path": "^1.0.0", @@ -29344,6 +29337,13 @@ "vlq": "^1.0.0" } }, + "ngx-material-file-input": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ngx-material-file-input/-/ngx-material-file-input-2.1.1.tgz", + "integrity": "sha512-FbaIjiJnL6BZtZYWLvMSn9aSaM62AZaJegloTUphmLz5jopXPzE5W+3aC+dsf9h1IIqHSCLcyv0w+qH0ypBhMA==", + "dev": true, + "requires": {} + }, "nice-try": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", diff --git a/frontend/package.json b/frontend/package.json index 41e3ad4..df74520 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -60,6 +60,7 @@ "karma-coverage-istanbul-reporter": "~2.1.1", "karma-jasmine": "~2.0.1", "karma-jasmine-html-reporter": "^1.5.4", + "ngx-material-file-input": "^2.1.1", "protractor": "~5.4.4", "ts-node": "~8.5.4", "tslint": "~5.20.1", diff --git a/frontend/src/app/app.module.ts b/frontend/src/app/app.module.ts index 71e6116..33d2221 100644 --- a/frontend/src/app/app.module.ts +++ b/frontend/src/app/app.module.ts @@ -27,29 +27,31 @@ import { NavigationComponent } from './component/navigation/navigation.component import {MatSnackBarModule} from '@angular/material/snack-bar'; import {MatCheckboxModule} from '@angular/material/checkbox'; import { EditierenComponent } from './component/einstellungen/editieren/editieren.component'; +import {MaterialFileInputModule} from 'ngx-material-file-input'; @NgModule({ declarations: [LandingComponent, LoginComponent, NavigationComponent, TweetsComponent, EinstellungenComponent, EditierenComponent], - imports: [ - ReactiveFormsModule, - BrowserModule, - BrowserAnimationsModule, - AppRoutingModule, - LoggerModule.forRoot({level: environment.log_level, serverLogLevel: NgxLoggerLevel.ERROR}), - HttpClientModule, - MatFormFieldModule, - FormsModule, - MatButtonModule, - MatInputModule, - MatSlideToggleModule, - MatSliderModule, - MatToolbarModule, - MatIconModule, - MatMenuModule, - MatSnackBarModule, - MatCheckboxModule - ], + imports: [ + ReactiveFormsModule, + BrowserModule, + BrowserAnimationsModule, + AppRoutingModule, + LoggerModule.forRoot({level: environment.log_level, serverLogLevel: NgxLoggerLevel.ERROR}), + HttpClientModule, + MatFormFieldModule, + FormsModule, + MatButtonModule, + MatInputModule, + MatSlideToggleModule, + MatSliderModule, + MatToolbarModule, + MatIconModule, + MatMenuModule, + MatSnackBarModule, + MatCheckboxModule, + MaterialFileInputModule + ], // enables injecting providers: [ AuthService, diff --git a/frontend/src/app/component/einstellungen/editieren/editieren.component.html b/frontend/src/app/component/einstellungen/editieren/editieren.component.html index 3f76b46..66b7f25 100644 --- a/frontend/src/app/component/einstellungen/editieren/editieren.component.html +++ b/frontend/src/app/component/einstellungen/editieren/editieren.component.html @@ -2,34 +2,42 @@
RSS-Feed erstellen
-