Commit 37ad5c1b authored by Racct's avatar Racct
Browse files

update for customer

parent 67ed9f84
Pipeline #1368 passed with stages
in 10 minutes and 1 second
......@@ -25,3 +25,6 @@
[submodule "imports/git_modules/forms"]
path = imports/git_modules/forms
url = ../../orga/modules/forms.git
[submodule "imports/git_modules/file-utils"]
path = imports/git_modules/file-utils
url = ../../orga/modules/file-utils.git
......@@ -26,11 +26,15 @@ generate('ccb', {
},
button: {
...tokens(spacing).box,
background: 'dark-grey',
height: '48px',
width: '100px',
borderStyle: 'solid',
background: 'lightgrey',
zIndex: '999',
padding: spacing.sm,
fontWeight: 'bold',
color: 'black',
cursor: 'pointer',
'[active]': {
background: 'lightgreen',
},
},
stack: {
...tokens(spacing).stack,
......
import { element, html } from '@adornis/base';
import { element, html, rerender } from '@adornis/base';
import { AdornisDBView, AttributeField } from '@adornis/adornis-components/a-dbview';
import { Content } from 'imports/db/contents';
import { BehaviorSubject } from 'rxjs';
import { BehaviorSubject, of, combineLatest } from 'rxjs';
import { ifDefined } from 'lit-html/directives/if-defined';
import { takeUntil, distinctUntilChanged, map, switchMap } from 'rxjs/operators';
import { takeUntil, distinctUntilChanged, map, switchMap, startWith } from 'rxjs/operators';
import { RenderProps } from '@adornis/base/reactive-lit-element';
import { rxRepeat } from '@adornis/base/rx-repeat';
import { spacing } from '@adornis/design/generate-base/tokens';
import { AdornisUser } from '@adornis/users/db';
const moment = require('moment');
import '@material/mwc-textfield';
import '@material/mwc-icon-button';
import '@adornis/forms/a-file-input';
import '@adornis/adornis-components/a-drop-wrapper';
import '@material/mwc-checkbox';
import { virtualize } from '@adornis/base/when-seen';
import { AdornisDialog } from 'imports/git_modules/adornis-components/a-dialog';
@element('ccb-contents-admin')
export class CCBContentsAdmin extends AdornisDBView<Content> {
@rerender public filterForOwnEntities = new BehaviorSubject(true);
constructor() {
super();
this.baseQLClass.next(Content);
AdornisUser.currentUser
.pipe(takeUntil(this.disconnected))
.subscribe(user => this.entities.next(Content.find({ createdBy: user?._id }).whenReady));
// AdornisUser.currentUser
// .pipe(takeUntil(this.disconnected))
// .subscribe(user => this.entities.next(Content.find({ createdBy: user?._id }).whenReady));
this.selectedFields
.pipe(
......@@ -33,43 +41,124 @@ export class CCBContentsAdmin extends AdornisDBView<Content> {
});
}
public render({ fields, selectedFields, entities, sorting }: RenderProps<AdornisDBView<Content>>) {
public render({ fields, selectedFields, entities, sorting, filterForOwnEntities }: RenderProps<CCBContentsAdmin>) {
return html`
${this.searchBar()}
<a-stack size="md" style=${{ padding: `0 ${spacing.xxl}` }}>
${this.addNewEntry(fields)} Du hast
${this.entities.pipe(switchMap(ents => ents.pipe(map(ents => ents.length))))} von insgesamt
${Content.count({}).whenReady} Einträgen erstellt. Der durchschnittliche Nutzer erstellt
${Content.count({}).whenReady.pipe(
switchMap(count => AdornisUser.count({}).whenReady.pipe(map(userCount => count / userCount))),
)}
Einträge.
${this.addNewEntry(fields)}
<ccb-text
>Du hast
${this.entities.pipe(
startWith(of([])),
switchMap(ents => ents.pipe(map(ents => ents.length))),
)}
von insgesamt ${Content.count({}).whenReady} Einträgen erstellt. Der durchschnittliche Nutzer erstellt
${Content.count({}).whenReady.pipe(
switchMap(count => AdornisUser.count({}).whenReady.pipe(map(userCount => (count / userCount).toFixed(2)))),
)}
Einträge.</ccb-text
>
${this.filterForOwn()} ${this.csvUploadButton()}
</a-stack>
<a-stack size="md" padding="xxl">
${rxRepeat(entities.pipe(map(ents => ents.sort(this.sortFunctionWithSortObject(sorting)))), (entity, index) =>
this.itemCard(entity, selectedFields, index),
${rxRepeat(
combineLatest(entities, AdornisUser.currentUser).pipe(
map(([ents, user]) =>
ents
.filter(ent => !filterForOwnEntities || !user || user._id === ent.createdBy)
.sort(this.sortFunctionWithSortObject(sorting)),
),
),
(entity, index) => this.itemCard(entity, selectedFields, index),
)}
</a-stack>
`;
}
protected itemCard(entity: Content, selectedFields: string[], index: number) {
let fileToUpload: File;
return html`
<ccb-stack padding="lg" size="md" elevation="2" class="${this.css({ position: 'relative' })}">
${this.fieldDisplays(this.getField('Rechtsgebiet')!, entity, index)}
${this.fieldDisplays(this.getField('Frage')!, entity, index)}
${this.fieldDisplays(this.getField('Antwort')!, entity, index)}
${this.fieldDisplays(this.getField('Quelle')!, entity, index)}
${this.fieldDisplays(this.getField('Ansprechpartner')!, entity, index)}
<div
class="${this.css({
position: 'absolute',
top: spacing.sm,
right: spacing.sm,
})}"
>
${this.saveField(entity, index)}
</div>
<ccb-stack
padding="lg"
size="md"
elevation="2"
class="${AdornisUser.currentUser.pipe(
map(user =>
this.css({
position: 'relative',
...(!user || user._id !== entity.createdBy ? { pointerEvents: 'none', opacity: '0.5' } : {}),
}),
),
)}"
>
${virtualize(html`
${this.fieldDisplays(this.getField('Rechtsgebiet')!, entity, index)}
${this.fieldDisplays(this.getField('Frage')!, entity, index)}
${this.fieldDisplays(this.getField('Antwort')!, entity, index)}
${this.fieldDisplays(this.getField('Quelle')!, entity, index)}
<a-stack horizontal centeraligned size="md">
${this.fieldDisplays(this.getField('Ansprechpartner')!, entity, index)}
<a-box
padding="md"
@click="${() =>
AdornisDialog.showPopup(
resolve => html`
<a-box vertical elevation="2">
${rxRepeat(
AdornisUser.find().whenReady,
user => html`<a-box padding="md" style="${{
background: 'white',
maxHeight: '50vh',
overflow: 'auto',
}}" @click="${() => {
if (entity.createdBy !== user._id) entity.createdBy = user._id;
resolve();
}}">
<ccb-h4>${user.username}</ccb-h4>
</a-box>
</a-box>
`,
)}</a-box
>
`,
)}"
>
<ccb-h4
>${entity.whenChanging.pipe(
switchMap(ent =>
AdornisUser.findOne({ _id: ent.createdBy }).whenReady.pipe(
map(user => (user ? 'Autor: ' + user.username : 'Autor zuweisen')),
),
),
)}</ccb-h4
>
</a-box>
</a-stack>
<ccb-stack horizontal centeraligned size="md">
<a-file-input @file-changed="${e => (fileToUpload = e.detail.file)}"></a-file-input>
<ccb-button
@click="${() => {
if (fileToUpload) {
entity.upload(fileToUpload);
}
}}"
>Datei hochladen</ccb-button
>
</ccb-stack>
<ccb-button @click="${() => entity.save()}"
>Diesen Inhalt als "aktuell" markieren (letzte Aktualisierung
${moment(entity.updateAt).format('DD.MM.YYYY HH:mm')})</ccb-button
>
<div
class="${this.css({
position: 'absolute',
top: spacing.sm,
right: spacing.sm,
})}"
>
${this.saveField(entity, index)}
</div>
`)}
</ccb-stack>
`;
}
......@@ -142,6 +231,25 @@ export class CCBContentsAdmin extends AdornisDBView<Content> {
}
}
private askForFile = (accept?: string): Promise<string> => {
return new Promise(resolve => {
const input = document.createElement('input');
input.type = 'file';
if (accept) input.accept = accept;
input.onchange = () => {
const fr = new FileReader();
fr.onload = e => resolve(fr.result);
fr.readAsText(
input.files![0],
navigator.platform.indexOf('Linux') !== -1 || navigator.platform.indexOf('Mac') !== -1
? 'UTF-8'
: 'ISO-8859-1',
);
};
input.click();
});
};
protected csvUploadButton() {
return html`
<ccb-button
......@@ -154,8 +262,42 @@ export class CCBContentsAdmin extends AdornisDBView<Content> {
borderColor: 'green',
borderRadius: '24px',
})}"
href="https://t.me/coronalegalchatbot"
>Los gehts!</ccb-button
@click="${async () => {
const contents = await this.askForFile('*.csv');
await Promise.all(
contents
.split('\n')
.slice(1)
.map(async line => {
const [type, question, answer, deepnessLevel, date, source, contactName, contactMail] = line.split(';');
// Check if content is new
let contentCheck = await Content.findOne({ Rechtsgebiet: type, Frage: question }).result;
if (!contentCheck) {
contentCheck = new Content({
Rechtsgebiet: type,
Frage: question,
Antwort: answer,
Quelle: source,
Ansprechpartner: contactName + ', ' + contactMail,
});
contentCheck.save().subscribe(console.log);
} else console.log('[UPLOAD] Mult. content skipped - ID:', contentCheck._id, 'Q:', contentCheck.Frage);
}),
);
}}"
>CSV hochladen!</ccb-button
>
`;
}
protected filterForOwn() {
return html`
<ccb-button
class="${this.css({ alignItems: 'center', display: 'flex', flexDirection: 'row' })}"
?active="${this.filterForOwnEntities.getValue()}"
@click="${() => this.filterForOwnEntities.next(!this.filterForOwnEntities.getValue())}"
><mwc-checkbox .checked="${this.filterForOwnEntities.getValue()}"></mwc-checkbox>Filter nach eigenen
Inhalten</ccb-button
>
`;
}
......
......@@ -5,6 +5,7 @@ import { createEmotionComponent, EmotionMixin } from '@adornis/design';
import { RenderProps } from '@adornis/base/reactive-lit-element';
import { BehaviorSubject } from 'rxjs';
import { AdornisUser } from '@adornis/users/db';
import { map } from 'rxjs/operators';
const smallImgStyle = { width: '50px', height: '50px' };
// createEmotionComponent('a-small-img', smallImgStyle);
......@@ -30,11 +31,14 @@ export class ccbTopbar extends EmotionMixin(ScopingElement) {
centeraligned
style="box-shadow: 0 0 10px black; padding: 0 ${spacing.md}; height: 100%"
>
<ccb-h3
class="${this.css({ cursor: 'pointer', userSelect: 'none' })}"
@click="${() => this.fireEvent('toggle-sidebar')}"
><mwc-icon>menu</mwc-icon></ccb-h3
>
<ccb-stack horizontal centeraligned size="md">
<ccb-h3
class="${this.css({ cursor: 'pointer', userSelect: 'none' })}"
@click="${() => this.fireEvent('toggle-sidebar')}"
><mwc-icon>menu</mwc-icon></ccb-h3
>
<ccb-text>${AdornisUser.currentUser.pipe(map(user => (user ? user.username : '')))}</ccb-text>
</ccb-stack>
<a-small-img
src="/coronalogo.png"
class="${this.css({ cursor: 'pointer', zIndex: '3' })}"
......
......@@ -30,6 +30,11 @@ export class MainElement extends Loader(AdornisLayout) {
}
public leftSidebar() {
return html`
<div
style="position: absolute; top: 0; left: 0; width: 100%; background-color: red; color: white; font-weight: bold; z-index: 10000;"
>
Hier nicht eintragen, Testumgebung
</div>
<div class="${this.css({ height: '100%', background: 'white', paddingTop: '12px' })}">
<a-stack size="md" padding="md" class="${this.css({ background: 'white' })}">
<ccb-h3
......
......@@ -5,20 +5,29 @@ import { AdornisUser } from '@adornis/users/db';
import { ID } from '@adornis/baseql/baseqlTypes';
import { AdornisEntity } from '@adornis/baseql/entities/adornis.type';
import { isClient, isServer } from '@adornis/baseql/isServer-isClient';
import { AdornisFile, files } from '@adornis/file-utils/db';
export const contents = new Mongo.Collection('contents');
if (isServer())
contents._ensureIndex({ Rechtsgebiet: 'text', Frage: 'text', Antwort: 'text', Ansprechpartner: 'text' });
if (isServer()) files._ensureIndex({ Rechtsgebiet: 'text', Frage: 'text', Antwort: 'text', Ansprechpartner: 'text' });
@Entity()
export class Rating extends AdornisEntity {
public static _name = 'Rating';
@Field(Boolean)
public rating!: boolean;
@Field(Date, { preSaveHook: dateNow() })
public createdAt!: Date;
}
@Entity({
collection: contents,
// collection: contents,
// publish: (user: AdornisUser) => {
// console.log('publish for user_id: ', user?._id);
// if (!user) return nothingSelector;
// return { createdBy: user._id };
// },
})
export class Content extends MongoEntity<Content>() {
export class Content extends AdornisFile {
public static _name = 'Content';
@Field(String)
......@@ -31,6 +40,12 @@ export class Content extends MongoEntity<Content>() {
public createdAt!: Date;
@Field(String)
public Quelle?: string;
@Field(String, {
preSaveHook: (fieldValue: any, fieldKey: string, entity: AdornisEntity) => {
return entity.crawlSource();
},
})
public crawlContent?: string;
@Field(String)
public Ansprechpartner?: string;
@Field(ID, {
......@@ -39,4 +54,25 @@ export class Content extends MongoEntity<Content>() {
},
})
public createdBy?: string;
@Field(Date, { preSaveHook: dateNow(true) })
public updateAt!: Date;
@Field([Rating], { default: [] })
public ratings!: Rating[];
public rate(positive: boolean) {
this.ratings.push(new Rating({ rating: positive }));
this.save();
}
public crawlSource() {
if (!this.Quelle || !this.Quelle.length) return;
const request = require('request-promise-native');
return request(this.Quelle, { json: true })
.then(res => {
console.log(res);
return res;
})
.catch(err => console.log(err));
}
}
Subproject commit afd03575456330ca22553e49ecf9d42efacb4b8c
Subproject commit ff98657077ebdf24acb5cf0df0ad3aeeffedea45
Subproject commit c620b82dc5e0aaee6c2239947c26134a3e6c76be
import { WebApp } from 'meteor/webapp';
import { WebhookClient, Suggestion, Card, Platforms, Payload } from 'dialogflow-fulfillment';
import { Card, Suggestion, WebhookClient } from 'dialogflow-fulfillment';
import express from 'express';
import { Content } from 'imports/db';
import { Carousel, BrowseCarouselItem, RichResponse } from 'actions-on-google';
import { WebApp } from 'meteor/webapp';
import { Collection } from 'mongodb';
import { files } from '@adornis/file-utils/db';
process.env.DEBUG = 'dialogflow:debug'; // enables lib debugging statements
......@@ -37,19 +37,22 @@ expressApp.post('/api/dialogflow-webhook', (request, response, next) => {
break;
}
default: {
// const content = await Content.findOne({
// $or: Content.getOwnFields()
// .map(f => f.name)
// .map(f => ({ $or: agent.query.split(' ').map(word => ({ [f]: { $regex: word, $options: 'ig' } })) })),
// }).result;
const contents = await Content.find({ $text: { $search: agent.query } }).result;
if (contents.length) {
agent.add(
'Hierzu gibt es sehr viele Ergebnisse und keine Überkategorie, versuch deine Frage ein wenig genauer zu formulieren :) (wenn wir es schaffen, hier ein carousel von Möglichkeits-Previews anzeigen)',
);
return;
if (!agent.query) {
agent.add('Oops... Hier gibts wohl noch einen Bug');
}
const content = contents[0];
const foundContents = await (files.rawCollection() as Collection)
.find({ $text: { $search: agent.query } })
.project({ score: { $meta: 'textScore' } })
.sort({ score: { $meta: 'textScore' } })
.toArray();
console.log(foundContents.map(x => x.score));
// if (foundContents.length > 20) {
// agent.add(
// `Hierzu gibt es sehr viele Ergebnisse (${foundContents.length}) und keine Überkategorie, versuch deine Frage ein wenig genauer zu formulieren :) (wenn wir es schaffen, hier ein carousel von Möglichkeits-Previews anzeigen)`,
// );
// return;
// }
const content = foundContents[0];
if (!content) {
agent.add('Da kann ich dir nicht helfen');
return;
......@@ -67,6 +70,7 @@ expressApp.post('/api/dialogflow-webhook', (request, response, next) => {
agent.add('Konnten wir dir damit weiterhelfen?');
agent.add(new Suggestion('Ja, danke!'));
agent.add(new Suggestion('nicht was ich meinte'));
agent.add(new Suggestion('Suche verfeinern'));
// agent.add(new Suggestion(`Quick Reply`));
// agent.add(new Suggestion(`Suggestion`));
......
import { registerFileUpload } from '@adornis/file-utils/server';
registerFileUpload({});
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment