Skip to content

Commit

Permalink
Merge pull request #28 from openstad/export-import-likes-and-argument…
Browse files Browse the repository at this point in the history
…s-with-ideas

Export import likes and arguments with ideas
  • Loading branch information
rudivanhierden authored Mar 12, 2024
2 parents 2f14c6d + cb8a78e commit 954c98e
Show file tree
Hide file tree
Showing 5 changed files with 160 additions and 73 deletions.
72 changes: 62 additions & 10 deletions src/components/ImportButton/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -61,11 +61,11 @@ export const ImportButton = (props) => {

const handleSubmit = (callback, afterSucces) => {
setImporting(true);

let apiValidationErrors = [];
const ideas = bundleArgumentsAndVotesWithIdea(values);

Promise.all(
values.map((value) => callback(value).catch((error, response) => {
ideas.map((value) => callback(value).catch((error, response) => {
var valueKeys = Object.keys(value);
var formattedFirstValue = valueKeys[0] && value[valueKeys[0]];
var formattedSecondValue = valueKeys[1] && value[valueKeys[1]];
Expand Down Expand Up @@ -138,15 +138,67 @@ export const ImportButton = (props) => {
return value;
}

const handleSubmitCreate = async () => {
const callback = (value) => {
// add Id key to remove
value = prepareData(value, ['id']);
value.tags = value.tags ? value.tags.split(",").map(name => name.trim()) : [];
return dataProvider.create(resource, { data: value });
};

handleSubmit(callback);
function bundleArgumentsAndVotesWithIdea(values) {
const idMapping = new Map();

values.forEach(value => {
if(idMapping.has(value.id)) {
const idea = idMapping.get(value.id);

// Add argument to list
if(value.argument_description) {
idea.arguments.push({
userId: value.argument_userId,
description: value.argument_description,
sentiment: value.argument_sentiment,
});
}

// Add vote to list
if(value.vote_userId) {
idea.votes.push({
userId: value.vote_userId,
opinion: value.vote_opinion,
})
}
idMapping.set(value.id, idea);
} else {
value.arguments = [];
value.votes = [];

if(value.argument_description) {
value.arguments.push({
userId: value.argument_userId,
description: value.argument_description,
sentiment: value.argument_sentiment,
});
}

if(value.vote_userId) {
value.votes.push({
userId: value.vote_userId,
opinion: value.vote_opinion,
})
}
idMapping.set(value.id, value);
}
});
return Array.from(idMapping.values());
}

const handleSubmitCreate = async () => {
const callback = (value) => {
// add Id key to remove
value = prepareData(value, ['id']);
value.tags = value.tags ? value.tags.split(",").map(name => name.trim()) : [];

console.log({value});
return dataProvider.create(resource, { data: value });
};

handleSubmit(callback);

};

const handleSubmitOverwrite = async () => {
Expand Down
18 changes: 15 additions & 3 deletions src/resources/argument.jsx
Original file line number Diff line number Diff line change
@@ -1,17 +1,29 @@
import React from 'react';
import { ReferenceInput, SelectInput, NumberInput, Datagrid, Edit, Create, SimpleForm, DateField, TextField, EditButton, TextInput, DateInput, ImageInput, ImageField, FunctionField, TopToolbar, CreateButton, ExportButton } from 'react-admin';
import { ReferenceInput, SelectInput, NumberInput, Datagrid, Edit, Create, SimpleForm, DateField, TextField, EditButton, TextInput, DateInput, ImageInput, ImageField, FunctionField, TopToolbar, CreateButton } from 'react-admin';
import ListAltIcon from '@material-ui/icons/ListAlt';
import {CustomList as List} from '../components/CustomList/index.jsx';
import { ExportButtons as EportExtendedButtons } from './export-ideas-with-arguments.jsx'
import { exporter, ExportButtons } from '../utils/export.jsx';
import { ExportButton as CustomExportButton} from './export-ideas-with-extras.jsx';


export const ArgumentIcon = ListAltIcon;

const EditTopToolbar = function({ basePath, total, data, resource }) {
return (
<TopToolbar>
<ExportButtons total={total} data={data} filename='tags'/>
<EportExtendedButtons data={data}/>
<CustomExportButton
label="Export ideas with arguments and likes (csv)"
withArguments={true}
withVotes={true}
extension="csv"
/>
<CustomExportButton
label="Export ideas with arguments and likes (xlsx)"
withArguments={true}
withVotes={true}
extension="xlsx"
/>
</TopToolbar>);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,30 @@ import React from 'react';
import { Button, downloadCSV } from 'react-admin';
import jsonExport from 'jsonexport/dist';
import Icon from "@material-ui/icons/ImportExport";
import DownloadIcon from '@material-ui/icons/GetApp';

import { useDataProvider } from 'react-admin';
import XLSX from 'xlsx';

export const ExportButtons = function(props) {
import {
useListContext,
} from 'ra-core';

export const ExportButton = function(props) {
const dataProvider = useDataProvider();
const {
filter,
filterValues,
currentSort,
exporter: exporterFromContext,
total,
} = useListContext(props);

let exporter = async function(data, id, type) {

let exporter = async function(data, id, type) {
let json = await dataProvider.getIdeasWithArgumentsAndLikes({...filter,
...filterValues});

let json = await dataProvider.getIdeasWithArguments({})
let ideas = json.data;

const exportHeaders = [
Expand All @@ -35,17 +49,17 @@ export const ExportButtons = function(props) {
{key: 'yes', label: 'Votes for'},
];

ideas.map((idea) => {
ideas.forEach((idea) => {
idea.location = idea.location ? ( idea.location.coordinates[0] + ', ' + idea.location.coordinates[1] ) : '';
});

let body = [];

ideas.map((idea) => {

ideas.forEach((idea) => {
let ideaLines = [];

const argLines = [];
let ideaLine = {};

exportHeaders.forEach((header) => {
if (header.userData) {
ideaLine[header.key] = idea.user && idea.user[header.key] ? idea.user[header.key] : '';
Expand All @@ -54,54 +68,52 @@ export const ExportButtons = function(props) {
}
});

if (idea.argumentsFor && idea.argumentsFor.length) {
let argLines = [];
idea.argumentsFor.sort( (a,b) => new Date(a.createdAt).getTime() - new Date(b.createdAt).getTime() ).forEach((arg) => {
let argLine = ideaLine || {};
ideaLine = undefined;
const ideaVotes = idea.votes || [];
const ideaArguments = (idea.argumentsFor || []).concat((idea.argumentsAgainst || []));


// Make a line in the csv for each argument of an idea
if(props.withArguments) {
ideaArguments.sort( (a,b) => new Date(a.createdAt).getTime() - new Date(b.createdAt).getTime())
.forEach((arg) => {
const argLine = ideaLine? Object.assign({}, ideaLine): {};
argLine.argument_userId = arg.userId;
argLine.argument_sentiment = arg.sentiment;
argLine.argument_description = arg.description.replace(/\r|\n/g, '\\n');
argLine.argument_username = arg.user.firstName + ' ' + arg.user.lastName;

if (arg.reactions && arg.reactions.length) {
arg.reactions.forEach((reaction) => {
let reactionLine = argLine || {};
argLine = undefined;
reactionLine.reaction_description = reaction.description.replace(/\r|\n/g, '\\n');
reactionLine.reaction_username = reaction.user.firstName + ' ' + reaction.user.lastName;
argLines.push(reactionLine)
argLines.push(reactionLine);
});
}
if (argLine) argLines.push(argLine); // no reactions have been added
});
ideaLines = ideaLines.concat(argLines);
}

if (idea.argumentsAgainst && idea.argumentsAgainst.length) {
let argLines = [];
idea.argumentsAgainst.sort( (a,b) => new Date(a.createdAt).getTime() - new Date(b.createdAt).getTime() ).forEach((arg) => {
let argLine = ideaLine || {};
ideaLine = undefined;
argLine.argument_sentiment = arg.sentiment;
argLine.argument_description = arg.description.replace(/\r|\n/g, '\\n');
argLine.argument_username = arg.user.firstName + ' ' + arg.user.lastName;
if (arg.reactions && arg.reactions.length) {
arg.reactions.forEach((reaction) => {
let reactionLine = argLine || {};
argLine = undefined;
reactionLine.reaction_description = reaction.description.replace(/\r|\n/g, '\\n');
reactionLine.reaction_username = reaction.user.firstName + ' ' + reaction.user.lastName;
argLines.push(reactionLine)
});
}
if (argLine) argLines.push(argLine); // no reactions have been added
// Make a line in the csv for each vote of an idea
if(props.withVotes) {
ideaVotes.forEach(vote => {
let argLine = ideaLine? Object.assign({}, ideaLine): {};
argLine.vote_userId = vote.userId;
argLine.vote_opinion= vote.opinion;
argLine.vote_userId = vote.userId;
if (argLine) argLines.push(argLine);
});
ideaLines = ideaLines.concat(argLines);
}

if (ideaLine) ideaLines.push(ideaLine); // no arguments have been added
if((!ideaVotes.length && !ideaArguments.length) || (!props.withVotes && !props.withArguments)) {
const argLine = ideaLine? Object.assign({}, ideaLine): {};
argLines.push(argLine);
}

ideaLine = undefined;
ideaLines = ideaLines.concat(argLines);
body = body.concat(ideaLines);


});

let filename = 'ideas-with-arguments';
Expand All @@ -123,22 +135,18 @@ export const ExportButtons = function(props) {

let idea_headers = exportHeaders.map( header => header.key );
jsonExport(body, {headers: [
...idea_headers, 'argument_sentiment', 'argument_description', 'argument_username', 'reaction_description', 'reaction_username'
...idea_headers, 'argument_sentiment', 'argument_description', 'argument_userId', 'argument_username', 'reaction_description', 'reaction_username'
]}, (err, csv) => {
downloadCSV(csv, `ideas-with-arguments`);
});

}

}

return (
<>
<Button label="Export ideas with arguments csv" onClick={data => exporter(data, props.id, 'csv')}>
<Icon/>
</Button>
<Button label="Export ideas with arguments xlsx" onClick={data => exporter(data, props.id, 'xlsx')}>
<Icon/>
<Button label={props.label} onClick={data => exporter(data, props.id, props.extension)}>
<DownloadIcon />
</Button>
</>);
}
Expand Down
25 changes: 11 additions & 14 deletions src/resources/idea/list.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import {
Pagination
} from 'react-admin';
import {CreateButton, ExportButton} from 'ra-ui-materialui';
import {ExportButton as CustomExportButton} from '../export-ideas-with-extras.jsx';
import jsonExport from 'jsonexport/dist';
import {CustomList as List} from '../../components/CustomList/index.jsx';
import { parseRowsForExport } from '../../utils/export.jsx';
Expand Down Expand Up @@ -159,22 +160,18 @@ export const ListActions = props => {
context: 'button',
})}
<CreateButton basePath={basePath}/>
<ExportButton
exporter={rows => exporter(rows, 'csv')}
disabled={total === 0}
maxResults={100000}
resource={resource}
sort={currentSort}
label="Export csv"
<CustomExportButton
label="Export as csv"
withArguments={true}
withVotes={true}
extension="csv"
filter={{...filterValues, ...permanentFilter}}
/>
<ExportButton
exporter={rows => exporter(rows, 'xlsx')}
disabled={total === 0}
maxResults={100000}
resource={resource}
sort={currentSort}
label="Export xlsx"
<CustomExportButton
label="Export as xlsx"
withArguments={true}
withVotes={true}
extension="xlsx"
filter={{...filterValues, ...permanentFilter}}
/>
<ExportButton
Expand Down
24 changes: 21 additions & 3 deletions src/simpleRestProvider.js
Original file line number Diff line number Diff line change
Expand Up @@ -215,9 +215,27 @@ export default (apiUrl, httpClient = fetchUtils.fetchJson) => (

// Ideas-with-Arguments specific calls ----------------------------------------------------------------------------------------------------

getIdeasWithArguments: (params) => {
const url = `${apiUrl}/idea?includeVoteCount=1&includeArguments=1&includeUser=1`;
return httpClient(url).then(({ headers, json }) => {
getIdeasWithArgumentsAndLikes: (params) => {
const filter = {};

if(params.id) {
filter.id = [params.id];
}

if(params.status) {
filter.status = [params.status];
}

const query = {
includeUser: 1,
includeVoteCount: 1,
includeArguments: 1,
includeVotes: 1,
filter: JSON.stringify(filter)
};

const url = `${apiUrl}/idea?${stringify(query)}`;
return httpClient(url, {}).then(({ headers, json }) => {
let result = {
data: json,
total: json.length
Expand Down

0 comments on commit 954c98e

Please sign in to comment.