Lint PHP code psr-12, add GH action

This commit is contained in:
Julien Nahum
2024-02-23 11:54:12 +01:00
parent e85e4df7fe
commit 62971a2ef4
226 changed files with 2338 additions and 2144 deletions

View File

@@ -3,9 +3,9 @@
namespace App\Console\Commands;
use App\Models\Forms\FormStatistic;
use Illuminate\Console\Command;
use App\Models\Forms\FormView;
use App\Models\Forms\FormSubmission;
use App\Models\Forms\FormView;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\DB;
class CleanDatabase extends Command
@@ -51,13 +51,13 @@ class CleanDatabase extends Command
->orderBy('date')
->groupBy('form_id', 'date')
->get()->each(function ($row) use (&$finalData) {
$finalData[$row->form_id."-".$row->date] = [
$finalData[$row->form_id.'-'.$row->date] = [
'form_id' => $row->form_id,
'date' => $row->date,
'data' => [
'views' => $row->views,
'submissions' => 0
]
'submissions' => 0,
],
];
});
@@ -68,7 +68,7 @@ class CleanDatabase extends Command
->orderBy('date')
->groupBy('form_id', 'date')
->get()->each(function ($row) use (&$finalData) {
$key = $row->form_id."-".$row->date;
$key = $row->form_id.'-'.$row->date;
if (isset($finalData[$key])) {
$finalData[$key]['data']['submissions'] = $row->submissions;
} else {
@@ -77,8 +77,8 @@ class CleanDatabase extends Command
'date' => $row->date,
'data' => [
'views' => 0,
'submissions' => $row->submissions
]
'submissions' => $row->submissions,
],
];
}

View File

@@ -24,9 +24,9 @@ class GenerateTemplate extends Command
*/
protected $description = 'Generates a new form template from a prompt';
const MAX_RELATED_TEMPLATES = 8;
public const MAX_RELATED_TEMPLATES = 8;
const FORM_STRUCTURE_PROMPT = <<<EOD
public const FORM_STRUCTURE_PROMPT = <<<'EOD'
You are an AI assistant for OpnForm, a form builder and your job is to build a form for our user.
Forms are represented as Json objects. Here's an example form:
@@ -157,7 +157,7 @@ class GenerateTemplate extends Command
Do not ask me for more information about required properties or types, only suggest me a form structure.
EOD;
const FORM_DESCRIPTION_PROMPT = <<<EOD
public const FORM_DESCRIPTION_PROMPT = <<<'EOD'
You are an AI assistant for OpnForm, a form builder and your job is to help us build form templates for our users.
Give me some valid html code (using only h2, p, ul, li html tags) for the following form template page: "[REPLACE]".
@@ -169,12 +169,12 @@ class GenerateTemplate extends Command
Each paragraph (except for the first one) MUST start with with a h2 tag containing a title for this paragraph.
EOD;
const FORM_SHORT_DESCRIPTION_PROMPT = <<<EOD
public const FORM_SHORT_DESCRIPTION_PROMPT = <<<'EOD'
I own a form builder online named OpnForm. It's free to use.
Give me a 1 sentence description for the following form template page: "[REPLACE]". It should be short and concise, but still explain what the form is about.
EOD;
const FORM_INDUSTRY_PROMPT = <<<EOD
public const FORM_INDUSTRY_PROMPT = <<<'EOD'
You are an AI assistant for OpnForm, a form builder and your job is to help us build form templates for our users.
I am creating a form template: "[REPLACE]". You must assign the template to industries. Return a list of industries (minimum 1, maximum 3 but only if very relevant) and order them by relevance (most relevant first).
@@ -185,7 +185,7 @@ class GenerateTemplate extends Command
Ex: { "industries": ["banking_forms","customer_service_forms"]}
EOD;
const FORM_TYPES_PROMPT = <<<EOD
public const FORM_TYPES_PROMPT = <<<'EOD'
You are an AI assistant for OpnForm, a form builder and your job is to help us build form templates for our users.
I am creating a form template: "[REPLACE]". You must assign the template to one or more types. Return a list of types (minimum 1, maximum 3 but only if very accurate) and order them by relevance (most relevant first).
@@ -196,17 +196,17 @@ class GenerateTemplate extends Command
Ex: { "types": ["consent_forms","award_forms"]}
EOD;
const FORM_QAS_PROMPT = <<<EOD
public const FORM_QAS_PROMPT = <<<'EOD'
Now give me 4 to 6 question and answers to put on the form template page. The questions should be about the reasons for this template (when to use, why, target audience, goal etc.).
The questions should also explain why OpnForm is the best option to create this form (open-source, free to use, integrations etc).
Reply only with a valid JSON, being an array of object containing the keys "question" and "answer".
EOD;
const FORM_TITLE_PROMPT = <<<EOD
public const FORM_TITLE_PROMPT = <<<'EOD'
Finally give me a title for the template. It must contain or end with "template". It should be short and to the point, without any quotes.
EOD;
const FORM_IMG_KEYWORDS_PROMPT = <<<EOD
public const FORM_IMG_KEYWORDS_PROMPT = <<<'EOD'
I want to add an image to illustrate this form template page. Give me a relevant search query for unsplash. Reply only with a valid JSON like this:
```json
{
@@ -227,14 +227,14 @@ class GenerateTemplate extends Command
->useStreaming()
->setSystemMessage('You are an assistant helping to generate forms.');
$completer->expectsJson()->completeChat([
["role" => "user", "content" => Str::of(self::FORM_STRUCTURE_PROMPT)->replace('[REPLACE]', $this->argument('prompt'))->toString()]
['role' => 'user', 'content' => Str::of(self::FORM_STRUCTURE_PROMPT)->replace('[REPLACE]', $this->argument('prompt'))->toString()],
]);
$formData = $completer->getArray();
$completer->doesNotExpectJson();
$formDescriptionPrompt = Str::of(self::FORM_DESCRIPTION_PROMPT)->replace('[REPLACE]', $this->argument('prompt'))->toString();
$formShortDescription = $completer->completeChat([
["role" => "user", "content" => Str::of(self::FORM_SHORT_DESCRIPTION_PROMPT)->replace('[REPLACE]', $this->argument('prompt'))->toString()]
['role' => 'user', 'content' => Str::of(self::FORM_SHORT_DESCRIPTION_PROMPT)->replace('[REPLACE]', $this->argument('prompt'))->toString()],
])->getString();
// If description is between quotes, remove quotes
$formShortDescription = Str::of($formShortDescription)->replaceMatches('/^"(.*)"$/', '$1')->toString();
@@ -250,30 +250,29 @@ class GenerateTemplate extends Command
// Now get description and QAs
$completer->doesNotExpectJson();
$formDescription = $completer->completeChat([
["role" => "user", "content" => $formDescriptionPrompt]
['role' => 'user', 'content' => $formDescriptionPrompt],
])->getHtml();
$completer->expectsJson();
$formCoverKeywords = $completer->completeChat([
["role" => "user", "content" => $formDescriptionPrompt],
["role" => "assistant", "content" => $formDescription],
["role" => "user", "content" => self::FORM_IMG_KEYWORDS_PROMPT]
['role' => 'user', 'content' => $formDescriptionPrompt],
['role' => 'assistant', 'content' => $formDescription],
['role' => 'user', 'content' => self::FORM_IMG_KEYWORDS_PROMPT],
])->getArray();
$imageUrl = $this->getImageCoverUrl($formCoverKeywords['search_query']);
$formQAs = $completer->completeChat([
["role" => "user", "content" => $formDescriptionPrompt],
["role" => "assistant", "content" => $formDescription],
["role" => "user", "content" => self::FORM_QAS_PROMPT]
['role' => 'user', 'content' => $formDescriptionPrompt],
['role' => 'assistant', 'content' => $formDescription],
['role' => 'user', 'content' => self::FORM_QAS_PROMPT],
])->getArray();
$completer->doesNotExpectJson();
$formTitle = $completer->completeChat([
["role" => "user", "content" => $formDescriptionPrompt],
["role" => "assistant", "content" => $formDescription],
["role" => "user", "content" => self::FORM_TITLE_PROMPT]
['role' => 'user', 'content' => $formDescriptionPrompt],
['role' => 'assistant', 'content' => $formDescription],
['role' => 'user', 'content' => self::FORM_TITLE_PROMPT],
])->getString();
$template = $this->createFormTemplate(
$formData,
$formTitle,
@@ -285,7 +284,7 @@ class GenerateTemplate extends Command
$types,
$relatedTemplates
);
$this->info('/form-templates/' . $template->slug);
$this->info('/form-templates/'.$template->slug);
// Set reverse related Templates
$this->setReverseRelatedTemplates($template);
@@ -298,34 +297,37 @@ class GenerateTemplate extends Command
*/
private function getImageCoverUrl($searchQuery): ?string
{
$url = 'https://api.unsplash.com/search/photos?query=' . urlencode($searchQuery) . '&client_id=' . config('services.unsplash.access_key');
$url = 'https://api.unsplash.com/search/photos?query='.urlencode($searchQuery).'&client_id='.config('services.unsplash.access_key');
$response = Http::get($url)->json();
$photoIndex = rand(0, max(count($response['results']) - 1, 10));
if (isset($response['results'][$photoIndex]['urls']['regular'])) {
return Str::of($response['results'][$photoIndex]['urls']['regular'])->replace('w=1080', 'w=600')->toString();
}
return null;
}
private function getIndustries(GptCompleter $completer, string $formPrompt): array
{
$industriesString = Template::getAllIndustries()->pluck('slug')->join(', ');
return $completer->completeChat([
["role" => "user", "content" => Str::of(self::FORM_INDUSTRY_PROMPT)
['role' => 'user', 'content' => Str::of(self::FORM_INDUSTRY_PROMPT)
->replace('[REPLACE]', $formPrompt)
->replace('[INDUSTRIES]', $industriesString)
->toString()]
->toString()],
])->getArray()['industries'];
}
private function getTypes(GptCompleter $completer, string $formPrompt): array
{
$typesString = Template::getAllTypes()->pluck('slug')->join(', ');
return $completer->completeChat([
["role" => "user", "content" => Str::of(self::FORM_TYPES_PROMPT)
['role' => 'user', 'content' => Str::of(self::FORM_TYPES_PROMPT)
->replace('[REPLACE]', $formPrompt)
->replace('[TYPES]', $typesString)
->toString()]
->toString()],
])->getArray()['types'];
}
@@ -344,21 +346,21 @@ class GenerateTemplate extends Command
}
});
arsort($templateScore); // Sort by Score
return array_slice(array_keys($templateScore), 0, self::MAX_RELATED_TEMPLATES);
}
private function createFormTemplate(
array $formData,
string $formTitle,
string $formDescription,
string $formShortDescription,
array $formQAs,
array $formData,
string $formTitle,
string $formDescription,
string $formShortDescription,
array $formQAs,
?string $imageUrl,
array $industry,
array $types,
array $relatedTemplates
)
{
array $industry,
array $types,
array $relatedTemplates
) {
// Add property uuids, improve form with options
foreach ($formData['properties'] as &$property) {
$property['id'] = Str::uuid()->toString(); // Column ID
@@ -387,13 +389,15 @@ class GenerateTemplate extends Command
'publicly_listed' => true,
'industries' => $industry,
'types' => $types,
'related_templates' => $relatedTemplates
'related_templates' => $relatedTemplates,
]);
}
private function setReverseRelatedTemplates(Template $newTemplate)
{
if (!$newTemplate || count($newTemplate->related_templates) === 0) return;
if (! $newTemplate || count($newTemplate->related_templates) === 0) {
return;
}
$templates = Template::whereIn('slug', $newTemplate->related_templates)->get();
foreach ($templates as $template) {

View File

@@ -27,34 +27,34 @@ class GenerateTaxExport extends Command
*/
protected $description = 'Compute Stripe VAT per country';
const EU_TAX_RATES = [
"AT" => 20,
"BE" => 21,
"BG" => 20,
"HR" => 25,
"CY" => 19,
"CZ" => 21,
"DK" => 25,
"EE" => 20,
"FI" => 24,
"FR" => 20,
"DE" => 19,
"GR" => 24,
"HU" => 27,
"IE" => 23,
"IT" => 22,
"LV" => 21,
"LT" => 21,
"LU" => 17,
"MT" => 18,
"NL" => 21,
"PL" => 23,
"PT" => 23,
"RO" => 19,
"SK" => 20,
"SI" => 22,
"ES" => 21,
"SE" => 25
public const EU_TAX_RATES = [
'AT' => 20,
'BE' => 21,
'BG' => 20,
'HR' => 25,
'CY' => 19,
'CZ' => 21,
'DK' => 25,
'EE' => 20,
'FI' => 24,
'FR' => 20,
'DE' => 19,
'GR' => 24,
'HU' => 27,
'IE' => 23,
'IT' => 22,
'LV' => 21,
'LT' => 21,
'LU' => 17,
'MT' => 18,
'NL' => 21,
'PL' => 23,
'PT' => 23,
'RO' => 19,
'SK' => 20,
'SI' => 22,
'ES' => 21,
'SE' => 25,
];
/**
@@ -69,20 +69,22 @@ class GenerateTaxExport extends Command
$endDate = $this->option('end-date');
// Validate the date format
if ($startDate && !Carbon::createFromFormat('Y-m-d', $startDate)) {
if ($startDate && ! Carbon::createFromFormat('Y-m-d', $startDate)) {
$this->error('Invalid start date format. Use YYYY-MM-DD.');
return Command::FAILURE;
}
if ($endDate && !Carbon::createFromFormat('Y-m-d', $endDate)) {
if ($endDate && ! Carbon::createFromFormat('Y-m-d', $endDate)) {
$this->error('Invalid end date format. Use YYYY-MM-DD.');
return Command::FAILURE;
} else if (!$endDate && $this->option('full-month')) {
} elseif (! $endDate && $this->option('full-month')) {
$endDate = Carbon::parse($startDate)->endOfMonth()->endOfDay()->format('Y-m-d');
}
$this->info('Start date: ' . $startDate);
$this->info('End date: ' . $endDate);
$this->info('Start date: '.$startDate);
$this->info('End date: '.$endDate);
$processedInvoices = [];
@@ -111,6 +113,7 @@ class GenerateTaxExport extends Command
// Ignore if payment was refunded
if (($invoice->payment_intent->status ?? null) !== 'succeeded') {
$paymentNotSuccessfulCount++;
continue;
}
@@ -132,14 +135,14 @@ class GenerateTaxExport extends Command
$aggregatedReport = $this->aggregateReport($processedInvoices);
$filePath = 'opnform-tax-export-per-invoice_' . $startDate . '_' . $endDate . '.xlsx';
$filePath = 'opnform-tax-export-per-invoice_'.$startDate.'_'.$endDate.'.xlsx';
$this->exportAsXlsx($processedInvoices, $filePath);
$aggregatedReportFilePath = 'opnform-tax-export-aggregated_' . $startDate . '_' . $endDate . '.xlsx';
$aggregatedReportFilePath = 'opnform-tax-export-aggregated_'.$startDate.'_'.$endDate.'.xlsx';
$this->exportAsXlsx($aggregatedReport, $aggregatedReportFilePath);
// Display the results
$this->info('Total invoices: ' . $totalInvoice . ' (with ' . $paymentNotSuccessfulCount . ' payment not successful or trial free invoice)');
$this->info('Total invoices: '.$totalInvoice.' (with '.$paymentNotSuccessfulCount.' payment not successful or trial free invoice)');
return Command::SUCCESS;
}
@@ -151,7 +154,7 @@ class GenerateTaxExport extends Command
foreach ($invoices as $invoice) {
$country = $invoice['cust_country'];
$customerType = is_null($invoice['cust_vat_id']) && $this->isEuropeanCountry($country) ? 'individual' : 'business';
if (!isset($aggregatedReport[$country])) {
if (! isset($aggregatedReport[$country])) {
$defaultVal = [
'count' => 0,
'total_usd' => 0,
@@ -163,7 +166,7 @@ class GenerateTaxExport extends Command
];
$aggregatedReport[$country] = [
'individual' => $defaultVal,
'business' => $defaultVal
'business' => $defaultVal,
];
}
$aggregatedReport[$country][$customerType]['count']++;
@@ -181,10 +184,11 @@ class GenerateTaxExport extends Command
$finalReport[] = [
'country' => $country,
'customer_type' => $customerType,
...$aggData
...$aggData,
];
}
}
return $finalReport;
}
@@ -226,7 +230,9 @@ class GenerateTaxExport extends Command
if ($taxRate = (self::EU_TAX_RATES[$countryCode] ?? null)) {
// If VAT ID is provided, then TAX is 0%
if (!$vatId) return $taxRate;
if (! $vatId) {
return $taxRate;
}
}
return 0;
@@ -241,12 +247,11 @@ class GenerateTaxExport extends Command
{
if (count($data) == 0) {
$this->info('Empty data. No file generated.');
return;
}
(new ArrayExport($data))->store($filename, 'local', \Maatwebsite\Excel\Excel::XLSX);
$this->line('File generated: ' . storage_path('app/' . $filename));
$this->line('File generated: '.storage_path('app/'.$filename));
}
}