-
Notifications
You must be signed in to change notification settings - Fork 0
As a user, I can upload keywords #6
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
base: develop
Are you sure you want to change the base?
Changes from all commits
b1010eb
456bff4
f020d47
2a073c6
1cca948
d725d00
df5cc14
dc80c70
41abc08
b57fd09
266d725
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,3 @@ | ||
| defmodule GoogleCrawler.Errors.FileNotSupportedError do | ||
| defexception message: "File is not supported" | ||
| end |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,73 @@ | ||
| defmodule GoogleCrawler.Search do | ||
| @moduledoc """ | ||
| The Search context. | ||
| """ | ||
|
|
||
| import Ecto.Query, warn: false | ||
| alias GoogleCrawler.Repo | ||
|
|
||
| alias GoogleCrawler.Search.Keyword | ||
| alias GoogleCrawler.Search.KeywordFile | ||
|
|
||
| @doc """ | ||
| Returns the list of keywords. | ||
|
|
||
| ## Examples | ||
|
|
||
| iex> list_keywords() | ||
| [%Keyword{}, ...] | ||
|
|
||
| """ | ||
| def list_keywords do | ||
| Repo.all(Keyword) | ||
| end | ||
|
|
||
| @doc """ | ||
| Gets a single keyword. | ||
|
|
||
| Raises `Ecto.NoResultsError` if the Keyword does not exist. | ||
|
|
||
| ## Examples | ||
|
|
||
| iex> get_keyword(123) | ||
| %Keyword{} | ||
|
|
||
| iex> get_keyword(456) | ||
| nil | ||
|
|
||
| """ | ||
| def get_keyword(id), do: Repo.get(Keyword, id) | ||
|
|
||
| @doc """ | ||
| Creates a keyword. | ||
|
|
||
| ## Examples | ||
|
|
||
| iex> create_keyword(%{field: value}) | ||
| {:ok, %Keyword{}} | ||
|
|
||
| iex> create_keyword(%{field: bad_value}) | ||
| {:error, %Ecto.Changeset{}} | ||
|
|
||
| """ | ||
| def create_keyword(attrs \\ %{}) do | ||
| %Keyword{} | ||
| |> Keyword.changeset(attrs) | ||
| |> Repo.insert() | ||
| end | ||
|
|
||
| @doc """ | ||
| Parses the keyword from the given file. | ||
| Returns the stream for each line in the csv file as [line_result]. | ||
| Raise an exception if the file mime type is not supported or the file parsing is failed. | ||
|
|
||
| ### Examples | ||
|
|
||
| iex > parse_keywords_from_file!("var/folder/abcdef", "text/csv") |> Enum.to_list | ||
| [ok: ["hotels"], ok: ["restaurants"]] | ||
|
|
||
| """ | ||
| def parse_keywords_from_file!(file_path, mime_type) do | ||
| KeywordFile.parse!(file_path, mime_type) | ||
| end | ||
| end |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,16 @@ | ||
| defmodule GoogleCrawler.Search.Keyword do | ||
| use Ecto.Schema | ||
| import Ecto.Changeset | ||
|
|
||
| schema "keywords" do | ||
| field :keyword, :string | ||
|
|
||
| timestamps() | ||
| end | ||
|
|
||
| def changeset(keyword, attrs \\ %{}) do | ||
| keyword | ||
| |> cast(attrs, [:keyword]) | ||
| |> validate_required([:keyword]) | ||
| end | ||
| end |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,38 @@ | ||
| defmodule GoogleCrawler.Search.KeywordFile do | ||
| use Ecto.Schema | ||
| import Ecto.Changeset | ||
|
|
||
| embedded_schema do | ||
| field :file, :map | ||
| end | ||
|
|
||
| @accept_file_ext ~w(.csv) | ||
|
|
||
| def changeset(keyword_file, attrs \\ %{}) do | ||
| keyword_file | ||
| |> cast(attrs, [:file]) | ||
| |> validate_required([:file]) | ||
| |> validate_file_ext() | ||
| end | ||
|
|
||
| def parse!(file_path, "text/csv") do | ||
| file_path | ||
| |> File.stream!() | ||
| |> CSV.decode!() | ||
| end | ||
|
|
||
| def parse!(_file_path, _unexpected_mime_type) do | ||
| raise GoogleCrawler.Errors.FileNotSupportedError, | ||
| message: "File with this extension is not supported" | ||
| end | ||
|
|
||
| defp validate_file_ext(changeset) do | ||
| validate_change(changeset, :file, fn :file, file -> | ||
| if Enum.member?(@accept_file_ext, Path.extname(file.filename)) do | ||
| [] | ||
| else | ||
| [file: "is not supported"] | ||
| end | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. How about: The return of an empty array
Owner
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I rechecked this one. I found something interesting 📝 When using the |
||
| end) | ||
| end | ||
| end | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,11 @@ | ||
| defmodule GoogleCrawlerWeb.DashboardController do | ||
| use GoogleCrawlerWeb, :controller | ||
|
|
||
| alias GoogleCrawler.Search.KeywordFile | ||
|
|
||
| def index(conn, _params) do | ||
| changeset = KeywordFile.changeset(%KeywordFile{}) | ||
|
|
||
| render(conn, "index.html", changeset: changeset) | ||
| end | ||
| end |
This file was deleted.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,23 @@ | ||
| defmodule GoogleCrawlerWeb.UploadController do | ||
| use GoogleCrawlerWeb, :controller | ||
| import Ecto.Changeset, only: [get_change: 3] | ||
|
|
||
| alias GoogleCrawler.Search | ||
| alias GoogleCrawler.Search.KeywordFile | ||
|
|
||
| def create(conn, %{"keyword_file" => keyword_file}) do | ||
| changeset = KeywordFile.changeset(%KeywordFile{}, keyword_file) | ||
|
|
||
| if changeset.valid? do | ||
| file = get_change(changeset, :file, nil) | ||
| result = Search.parse_keywords_from_file!(file.path, file.content_type) | ||
|
|
||
| # TODO: Save these keywords and triggers the task to google search for each keyword | ||
| text(conn, result |> Enum.map(fn keyword -> List.first(keyword) end) |> Enum.join(", ")) | ||
| else | ||
| conn | ||
| |> put_flash(:error, gettext("Invalid file, please select again.")) | ||
| |> redirect(to: Routes.dashboard_path(conn, :index)) | ||
| end | ||
| end | ||
| end |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,6 @@ | ||
| <%= render GoogleCrawlerWeb.KeywordView, "_form.html", assigns %> | ||
| <hr> | ||
| <section> | ||
| <h3><%= gettext("Keywords") %></h3> | ||
| <p><%= gettext("You don't have any keywords.") %></p> | ||
| </section> |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,11 @@ | ||
| <section> | ||
| <h3><%= gettext("Upload your keyword file (.csv)") %></h3> | ||
| <p><%= gettext("📝 Please put one keyword per line") %></p> | ||
| <%= form_for @changeset, Routes.upload_path(@conn, :create), [multipart: true], fn f -> %> | ||
| <%= label f, :file %> | ||
| <%= file_input f, :file, required: true %> | ||
| <%= error_tag f, :file %> | ||
|
|
||
| <%= submit gettext("Upload") %> | ||
| <% end %> | ||
| </section> |
This file was deleted.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,3 @@ | ||
| defmodule GoogleCrawlerWeb.DashboardView do | ||
| use GoogleCrawlerWeb, :view | ||
| end |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,3 @@ | ||
| defmodule GoogleCrawlerWeb.KeywordView do | ||
| use GoogleCrawlerWeb, :view | ||
| end |
This file was deleted.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,11 @@ | ||
| defmodule GoogleCrawler.Repo.Migrations.CreateKeywords do | ||
| use Ecto.Migration | ||
|
|
||
| def change do | ||
| create table(:keywords) do | ||
| add :keyword, :string | ||
|
|
||
| timestamps() | ||
| end | ||
| end | ||
| end |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,21 @@ | ||
| defmodule GoogleCrawler.KeywordFactory do | ||
| alias GoogleCrawler.Search | ||
|
|
||
| def default_attrs do | ||
| %{ | ||
| keyword: FakerElixir.Lorem.word() | ||
| } | ||
| end | ||
|
|
||
| def build_attrs(attrs \\ %{}) do | ||
| Enum.into(attrs, default_attrs()) | ||
| end | ||
|
|
||
| def create(attrs \\ %{}) do | ||
| keyword_attrs = build_attrs(attrs) | ||
|
|
||
| {:ok, keyword} = Search.create_keyword(keyword_attrs) | ||
|
|
||
| keyword | ||
| end | ||
| end |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,13 @@ | ||
| defmodule GoogleCrawler.KeywordFileFactory do | ||
| import GoogleCrawler.FixtureHelper | ||
|
|
||
| def default_attrs do | ||
| %{ | ||
| file: upload_file_fixture("keyword_files/invalid_keyword.csv") | ||
| } | ||
| end | ||
|
|
||
| def build_attrs(attrs \\ %{}) do | ||
| Enum.into(attrs, default_attrs()) | ||
| end | ||
| end |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,2 @@ | ||
| elixir, ruby | ||
| javascript |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| elixir ruby javascript |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,3 @@ | ||
| elixir | ||
| ruby | ||
| javascript |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's a great idea to use
embedded_schema. I just read about it last weekend. It's the closest to implement some kind of form object 💪